Created 2/22/21 , HRB

Updated 3/9/21 , Switched COVID Tracking Project Truth to download from 11/16/2020

Updated 4/11/21 , Use results which include PR

Read in forecasts and attach truth

myfips<-function(fips_codes,to="Abbreviation"){
  fips_abbrev <- fips_codes
  fips_abbrev[fips_codes!="US"] <- fips(fips_codes[fips_codes!="US"],to="Abbreviation") 
  fips_abbrev[fips_codes=="US"] <- " US"
  
  return(fips_abbrev)
  
}

Calculate interval scores for a specified level (alpha)

\[ \text{IS}_{\alpha}(F,y) = (u - \ell) + \frac{2}{\alpha} (\ell - y) \mathbf{1}(\ell > y) + \frac{2}{\alpha}(y - u)\mathbf{1}(u < y)\]

Calculate Weighted Interval Score (WIS)

\[ \text{WIS}_{\alpha_0 : K}(F,y) = \frac{1}{K+1/2}\left(w_0 \, |y-m| + \sum_{k=1}^K w_k \, \text{IS}_{\alpha_k}(F,y)\right) \] where \(w_0 = \frac 1 2\) and \(w_k = \frac{\alpha_k}{2}\) for \(k = 1,\hdots, K\) for \(\alpha= 0.02, 0.05, 0.1, 0.2, \hdots, 0.9\).

getWIS <- function(forecasts_with_truth){


  
  w0 = 0.5
  f_medians =  subset(forecasts_with_truth,quantile ==0.5,
                       select="value" )
  truth_y = subset(forecasts_with_truth,quantile ==0.5,
                       select="truth" )
  WIS <- w0*abs(truth_y-f_medians)
  
  for (alpha_level in c(0.02,0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9)){
    wk = alpha_level/2
    temp_IS <- getIntervalScores(forecasts_with_truth,alpha_level)
    
    WIS <- WIS + wk*temp_IS$interval_score
    
  }
  
    WIS_temp_df <- subset(forecasts_with_truth,quantile ==0.5,
                       select=c("forecast_date","target","target_end_date",
                                "location","truth") )
    WIS_temp_df$WIS <- WIS$truth
  
  
 
  
  return(WIS_temp_df)
}
EpiCovDA_WIS <- getWIS(forecasts_with_truth)

EpiCovDA_WIS$IS_05 <- getIntervalScores(forecasts_with_truth, 0.05)$interval_score

EpiCovDA_WIS$IS_50 <- getIntervalScores(forecasts_with_truth, 0.5)$interval_score

Attach state population

EpiCovDA_WIS$population <- NA
EpiCovDA_pt_scores$population <- NA



for (loc in unique(EpiCovDA_WIS$location)){
  if (loc == "US"){
    temp_pop <- as.numeric(read.csv("../state_hosp_data_2020-11-16/state_popUS.csv"))
    EpiCovDA_WIS[which(EpiCovDA_WIS$location=="US"),"population"] <- temp_pop
    EpiCovDA_pt_scores[which(EpiCovDA_pt_scores$location == "US"),"population"] <- temp_pop

    
  } else {

        temp_pop <- as.numeric(read.csv(sprintf("../state_hosp_data_2020-11-16/state_pop%s.csv",fips(loc, to = "Abbreviation"))))
    EpiCovDA_WIS[which(EpiCovDA_WIS$location==loc),"population"] <- temp_pop
    EpiCovDA_pt_scores[which(EpiCovDA_pt_scores$location == fips(loc, to = "Abbreviation")),"population"] <- temp_pop

  
  }
  
  
}
incomplete final line found by readTableHeader on '../state_hosp_data_2020-11-16/state_popUS.csv'
statsOnPerformance <- function(all_IS,column,tar_type = "death",filterdates_TF = TRUE,
                               includeUS = FALSE,
                               USonly = FALSE){
  
  all_targets <- c("1 wk ahead cum death","2 wk ahead cum death","3 wk ahead cum death",
                   "4 wk ahead cum death")
  
  if (tar_type == "case"){
    all_targets <- c("1 wk ahead cum case","2 wk ahead cum case",
                     "3 wk ahead cum case",
                     "4 wk ahead cum case")
  }
  
  
  
  if (filterdates_TF){
    all_IS <- subset(all_IS,(as.Date(forecast_date)>as.Date("2020-04-19")))
    
  }
  
  if (!includeUS){
    subset(all_IS,(location!="US"))
  }
  
  if (USonly){
    all_IS <- subset(all_IS,(location=="US"))
  }


  
  stats_df <- data.frame()
  
  temp_df <- data.frame(target = "Overall",mean = mean(all_IS[grepl(tar_type,all_IS$target),column]),
                        median = median(all_IS[grepl(tar_type,all_IS$target),column]))
  
  stats_df <- rbind(stats_df,temp_df)
  
  
  
  for (tar in all_targets){
    curr_subset <- subset(all_IS,target == tar)
    curr_mean <- mean(curr_subset[,column])
    curr_median <- median(curr_subset[,column])
    temp_df <- data.frame(target = tar, mean = curr_mean,median = curr_median)
    
    stats_df <- rbind(stats_df,temp_df)
  }
  
  return(stats_df)
}
IS_stats_EpiCovDA <- statsOnPerformance(EpiCovDA_WIS,"WIS")
# kable(IS_stats_EpiCovDA,booktabs=TRUE)
# kable(IS_stats_COVIDhub,booktabls=TRUE)

Point Scores

EpiCovDA_pt_scores$abs_error <- abs(EpiCovDA_pt_scores$error)

EpiCovDA_pt_stats <- statsOnPerformance(EpiCovDA_pt_scores,"abs_error")

Plot IS_alpha




ggplot()+
  geom_line(data=EpiCovDA_WIS[(EpiCovDA_WIS$target=="1 wk ahead cum death")&(EpiCovDA_WIS$location=="US"),],
            mapping=aes(x=as.Date(forecast_date),y=IS_05,group=location))




ggplot()+
    geom_line(data=subset(
                EpiCovDA_pt_scores[grepl("death",EpiCovDA_pt_scores$target),],location=="US"),
            mapping=aes(x=as.Date(forecast_date),y=abs_error,group=target,color=target))

NA
NA
IS_deaths_df <- EpiCovDA_WIS[grepl("death",EpiCovDA_WIS$target),]


thisfilename <- "results_figures/US_IS.pdf"

pdf(thisfilename, width = 10, height = 6)
a3 <- ggplot()+
  geom_line(data=IS_deaths_df[(IS_deaths_df$location=="US"),],
            mapping=aes(x=as.Date(forecast_date),y=IS_05,group=target,color=target))+
  xlab("Forecast Date")+ylab("Interval Score")+ggtitle("Interval Scores for US Death Forecasts")+
    scale_color_discrete(name="Target",labels = c("1 wk ahead cmltv deaths","2 wk ahead cmltv deaths","3 wk ahead cmltv deaths","4 wk ahead cmltv deaths"))+
  theme(text = element_text(size = 16))
print(a3)
dev.off()
null device 
          1 

Death Results: Week ahead interval score figures

Per 100,000 population

Max IS_05/population*10^5 is about 677


# for (i in 1:4){


mybreaks = c(0,1,5,10,25,50,100,500,Inf)
mylabels = c("<1","1 <= x < 5", "5 <= x < 10", "10 <= x < 25","25 <= x < 50","50 <= x < 100", "100 <= x < 500",">=500")

figfilename3 <- sprintf("results_figures/IS_epicovda_deaths.pdf")

pdf(figfilename3, width = 10, height = 7)
p1 <- ggplot()+
  geom_tile(data = IS_deaths_df,
            mapping =
              aes(as.Date(forecast_date),
                  myfips(as.character(location),to="Abbreviation"),
                  fill=cut(IS_05/population*10^5,breaks=mybreaks,right=FALSE,labels=mylabels))) +
  # scale_fill_gradient2(low = "white",high="firebrick3",mid="gold",trans="log10",midpoint=1,
  #                     breaks=mybreaks,labels=mybreaks)+
  scale_fill_brewer(type="seq",palette = "YlOrRd")+
  theme_bw()+
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())+
  xlab("Forecast Date")+ylab("State")+
  ggtitle("EpiCovDA Interval Scores (alpha=0.05) for Death Forecasts")+
  # ggtitle(sprintf("EpiCovDA, %s wk ahead cumulative deaths",i))+
  labs(fill="IS per 100,000")+
  facet_wrap(vars(target),scales="fixed",nrow=1)
print(p1)
dev.off()
null device 
          1 
print(p1)


# }

# max(IS_deaths_df$IS_05/IS_deaths_df$population*10^5)

Point Estimate Errors

Max cumulative deaths for 4 week ahead forecasts: about 28.9 per 100,000


mybreaks2 <- c(0,.5,1,5,10,15,25,Inf)
mylabels2 = c("<0.5","0.5 <= x < 1","1 <= x < 5", "5 <= x < 10", "10 <= x < 15","15 <= x < 25",">=25")



figfilename1 <- sprintf("results_figures/AE_epicovda_deaths.pdf")




pdf(figfilename1, width = 10, height = 7)
p3<- ggplot()+
  geom_tile(data = EpiCovDA_pt_scores[grepl("death",EpiCovDA_pt_scores$target),],
            mapping =
              aes(as.Date(forecast_date),
                  myfips(as.character(location),to="Abbreviation"),
                  fill=   cut(abs_error/population*10^5,breaks=mybreaks2,right=FALSE,labels=mylabels2) )) +
  # scale_fill_gradient2(low = "white",mid="gold",high="firebrick3",
  #                      trans="log10",midpoint = log10(.5),na.value = "white",breaks=mybreaks2,
  #                      labels=mybreaks2)+
  scale_fill_brewer(type="seq",palette = "YlOrRd")+
  theme_bw()+
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())+
  xlab("Forecast Date")+ylab("State")+ggtitle("EpiCovDA Absolute Error per 100,000 for Death Forecasts") +
  labs(fill="Abs. Error per 100,000")+
  facet_wrap(vars(target),scales="fixed",nrow=1)
print(p3)
dev.off()
null device 
          1 
print(p3)





# max(EpiCovDA_pt_scores[(EpiCovDA_pt_scores$target==sprintf("%s wk ahead cum death",j)),"abs_error"]/EpiCovDA_pt_scores[(EpiCovDA_pt_scores$target==sprintf("%s wk ahead cum death",j)),"population"]*10^5)

“Heatmaps” for Cases

max IS for cases about 23752 mediant IS for cases about 193


# for (i in 1:4){


mybreaks = c(0,5,25,100,300,1000,5000,10000,Inf)
mylabels = c("<5","5 <= x < 25", "25 <= x < 100", "100 <= x < 300","300 <= x < 1000","1000 <= x < 5000", "5000 <= x < 10000",">=10000")

figfilename3 <- sprintf("results_figures/IS_epicovda_cases.pdf")

pdf(figfilename3, width = 10, height = 7)
p1 <- ggplot()+
  geom_tile(data = EpiCovDA_WIS[grepl("case",EpiCovDA_WIS$target),],
            mapping =
              aes(as.Date(forecast_date),
                  myfips(as.character(location),to="Abbreviation"),
                  fill=cut(IS_05/population*10^5,breaks=mybreaks,right=FALSE,labels=mylabels))) +
  # scale_fill_gradient2(low = "white",high="firebrick3",mid="gold",trans="log10",midpoint=1,
  #                     breaks=mybreaks,labels=mybreaks)+
  scale_fill_brewer(type="seq",palette = "YlOrRd")+
  theme_bw()+
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())+
  xlab("Forecast Date")+ylab("State")+
  ggtitle("EpiCovDA Interval Scores (alpha=0.05) for Case Forecasts")+
  # ggtitle(sprintf("EpiCovDA, %s wk ahead cumulative deaths",i))+
  labs(fill="IS per 100,000")+
  facet_wrap(vars(target),scales="fixed",nrow=1)
print(p1)
dev.off()
null device 
          1 
print(p1)


# }

# median(EpiCovDA_WIS[grepl("case",EpiCovDA_WIS$target),"IS_05"]/EpiCovDA_WIS[grepl("case",EpiCovDA_WIS$target),"population"]*10^5)

Point Estimate Errors

Max cumulative deaths for 4 week ahead forecasts: about 867 per 100,000 median about 67


mybreaks2 <- c(0,1,10,25,50,100,300,500,Inf)
mylabels2 = c("<1","1 <= x < 10","10 <= x < 25", "25 <= x < 50", "50 <= x < 100","100 <= x < 300",
              "300 <= x < 500",">=500")



figfilename1 <- sprintf("results_figures/AE_epicovda_cases.pdf")




pdf(figfilename1, width = 10, height = 7)
p3<- ggplot()+
  geom_tile(data = EpiCovDA_pt_scores[grepl("case",EpiCovDA_pt_scores$target),],
            mapping =
              aes(as.Date(forecast_date),
                  myfips(as.character(location),to="Abbreviation"),
                  fill=   cut(abs_error/population*10^5,breaks=mybreaks2,right=FALSE,labels=mylabels2) )) +
  # scale_fill_gradient2(low = "white",mid="gold",high="firebrick3",
  #                      trans="log10",midpoint = log10(.5),na.value = "white",breaks=mybreaks2,
  #                      labels=mybreaks2)+
  scale_fill_brewer(type="seq",palette = "YlOrRd")+
  theme_bw()+
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())+
  xlab("Forecast Date")+ylab("State")+ggtitle("EpiCovDA Absolute Error per 100,000 for Case Forecasts") +
  labs(fill="Abs. Error per 100,000")+
  facet_wrap(vars(target),scales="fixed",nrow=1)
print(p3)
dev.off()
null device 
          1 
print(p3)





# median(EpiCovDA_pt_scores[(EpiCovDA_pt_scores$target==sprintf("%s wk ahead cum case",4)),"abs_error"]/EpiCovDA_pt_scores[(EpiCovDA_pt_scores$target==sprintf("%s wk ahead cum case",4)),"population"]*10^5)

forecasted_locations = unique(forecasts_with_truth$location)

tar_type = "case"

for (loc in forecasted_locations){

state_num = loc #"04"

target_forecasts_with_truth <-
  forecasts_with_truth[grepl(tar_type,
                             forecasts_with_truth$target) &
                         (forecasts_with_truth$location == state_num)&
                         as.Date(forecasts_with_truth$forecast_date)
                       >as.Date('2020-04-10'),]


st_ab = fips(state_num,to="Abbreviation")

if (loc == "US"){
  st_ab = "US"
}
fcasts_wide <- as_tibble(target_forecasts_with_truth) %>%
    filter(quantile %in% c(0.025, 0.1, 0.25, 0.5, 0.75, 0.9, 0.975)) %>%
    mutate(week_ahead = as.numeric(substr(target, 0,2))) %>%
    pivot_wider(names_from = quantile, names_prefix="q")



figfilename <- sprintf("results_figures/%s_%s_quant_forecasts.pdf",st_ab,tar_type)

pdf(figfilename, width = 5, height = 3)
a1 <- ggplot(fcasts_wide,aes(x = as.Date(target_end_date)))+
    geom_line(aes(y=q0.5, color=forecast_date, group=forecast_date)) +
    # geom_ribbon(aes(ymin=q0.1, ymax=q0.9, fill=forecast_date, group=forecast_date), alpha=.3) +
    geom_ribbon(aes(ymin=q0.025, ymax=q0.975, fill=forecast_date, group=forecast_date), alpha=.3) +
    geom_ribbon(aes(ymin=q0.25, ymax=q0.75, fill=forecast_date, group=forecast_date), alpha=.3) +
    geom_point(aes(y=truth)) +
    geom_line(aes(y=truth)) +
    theme_bw() + xlab("Date") +
    theme(legend.position = "none") + ylab(sprintf("cumulative %s",paste0(tar_type,"s"))) +
    ggtitle(sprintf("Cumulative %s in %s, observed and forecasted",paste0(tar_type,"s"),st_ab))
#+
    # coord_cartesian(ylim=c(250, 2500))

print(a1)
dev.off()
print(a1)



}


forecasted_locations = unique(forecasts_with_truth$location)

tar_type = "death"

for (loc in forecasted_locations){

state_num = loc #"04"

target_forecasts_with_truth <-
  forecasts_with_truth[grepl(tar_type,
                             forecasts_with_truth$target) &
                         (forecasts_with_truth$location == state_num)&
                         as.Date(forecasts_with_truth$forecast_date)
                       >as.Date('2020-04-10'),]


st_ab = fips(state_num,to="Abbreviation")

if (loc == "US"){
  st_ab = "US"
}
fcasts_wide <- as_tibble(target_forecasts_with_truth) %>%
    filter(quantile %in% c(0.025, 0.1, 0.25, 0.5, 0.75, 0.9, 0.975)) %>%
    mutate(week_ahead = as.numeric(substr(target, 0,2))) %>%
    pivot_wider(names_from = quantile, names_prefix="q")



figfilename <- sprintf("results_figures/%s_%s_quant_forecasts.pdf",st_ab,tar_type)

pdf(figfilename, width = 5, height = 3)
a1 <- ggplot(fcasts_wide,aes(x = as.Date(target_end_date)))+
    geom_line(aes(y=q0.5, color=forecast_date, group=forecast_date)) +
    # geom_ribbon(aes(ymin=q0.1, ymax=q0.9, fill=forecast_date, group=forecast_date), alpha=.3) +
    geom_ribbon(aes(ymin=q0.025, ymax=q0.975, fill=forecast_date, group=forecast_date), alpha=.3) +
    geom_ribbon(aes(ymin=q0.25, ymax=q0.75, fill=forecast_date, group=forecast_date), alpha=.3) +
    geom_point(aes(y=truth)) +
    geom_line(aes(y=truth)) +
    theme_bw() + xlab("Date") +
    theme(legend.position = "none") + ylab(sprintf("cumulative %s",paste0(tar_type,"s"))) +
    ggtitle(sprintf("Cumulative %s in %s, observed and forecasted",paste0(tar_type,"s"),st_ab))
#+
    # coord_cartesian(ylim=c(250, 2500))

print(a1)
dev.off()
print(a1)



}


get_calibration_prop <- function(forecasts_with_truth,alpha_level,conditions,target_type="death"){
  forecast_sub0 <- forecasts_with_truth[grepl(target_type,forecasts_with_truth$target),]
  forecast_subset <- subset(forecast_sub0,eval(conditions))
  lbounds = subset(forecast_subset,quantile==alpha_level/2,select="value")$value
  ubounds =  subset(forecast_subset,quantile==1 - alpha_level/2,select="value")$value
  truth =  subset(forecast_subset,quantile==0.5,select = "truth")$truth


  in_bounds <- ((ubounds - truth)>=0) & ((lbounds-truth)<=0)

  prop = sum(in_bounds)/length(truth)

  check_df <- cbind(lbounds,ubounds,truth,in_bounds)


  return(prop)

}

Calibration figures


for (i in 1:4){

curr_loc = "US"

cond_target = sprintf("%s wk ahead cum case",i)
condition = expression((target==cond_target)&(location=="US"))
tar_type = "case"
# title_text <- paste(curr_loc,"-",tar_type)
title_text <- sprintf("US - %s week ahead cumulative cases",i)



curr_loc_fips = fips(curr_loc)
if (curr_loc == "US"){
 curr_loc_fips = "US"
}
if (curr_loc_fips < 10){
  curr_loc_fips = paste0(0,curr_loc_fips)
}else{curr_loc_fips = as.character(curr_loc_fips)}

# condition = expression((location==curr_loc_fips))

all_props <- c()
all_CI_widths <- 1-c(0.02,0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9)

for (j in 1:length(all_CI_widths)){
  curr_a_level = round(1 - all_CI_widths[j],3)
  # curr_prop <- get_calibration_prop(forecasts_with_truth,curr_a_level,
  #                                   expression((location == curr_loc_fips)),"case")
  curr_prop <- get_calibration_prop(forecasts_with_truth,curr_a_level,
                                    condition,tar_type)
  all_props <- c(all_props,curr_prop)
}


calib_df <- data.frame(exp_prop = all_CI_widths,act_prop = all_props)


figfilename_calibration <- sprintf("results_figures/US_%swkaheadcases_calibration.pdf",i)

pdf(figfilename_calibration, width = 5, height = 3)
a3 <- ggplot(data=calib_df)+
  geom_point(mapping=aes(x=exp_prop,y=act_prop))+
  geom_abline(slope=1,intercept=0)+
  xlim(c(0,1))+ylim(c(0,1))+
  xlab("Expected Proportion")+
  ylab("Actual Proportion")+
  ggtitle(title_text)
print(a3)
dev.off()
}

for (i in 1:4){



cond_target = sprintf("%s wk ahead cum case",i)
condition = expression((target==cond_target)&(location!="US"))
tar_type = "case"
# title_text <- paste(curr_loc,"-",tar_type)
title_text <- sprintf("State-level - %s week ahead cumulative cases",i)



all_props <- c()
all_CI_widths <- 1-c(0.02,0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9)

for (j in 1:length(all_CI_widths)){
  curr_a_level = round(1 - all_CI_widths[j],3)
  # curr_prop <- get_calibration_prop(forecasts_with_truth,curr_a_level,
  #                                   expression((location == curr_loc_fips)),"case")
  curr_prop <- get_calibration_prop(forecasts_with_truth,curr_a_level,
                                    condition,tar_type)
  all_props <- c(all_props,curr_prop)
}


calib_df <- data.frame(exp_prop = all_CI_widths,act_prop = all_props)


figfilename_calibration <- sprintf("results_figures/state-level_%swkaheadcases_calibration.pdf",i)

pdf(figfilename_calibration, width = 5, height = 3)
a3 <- ggplot(data=calib_df)+
  geom_point(mapping=aes(x=exp_prop,y=act_prop))+
  geom_abline(slope=1,intercept=0)+
  xlim(c(0,1))+ylim(c(0,1))+
  xlab("Expected Proportion")+
  ylab("Actual Proportion")+
  ggtitle(title_text)
print(a3)
dev.off()
}

for (i in 1:4){

curr_loc = "US"

cond_target = sprintf("%s wk ahead cum death",i)
condition = expression((target==cond_target)&(location=="US"))
tar_type = "death"
# title_text <- paste(curr_loc,"-",tar_type)
title_text <- sprintf("US - %s week ahead cumulative deaths",i)



curr_loc_fips = fips(curr_loc)
if (curr_loc == "US"){
 curr_loc_fips = "US"
}
if (curr_loc_fips < 10){
  curr_loc_fips = paste0(0,curr_loc_fips)
}else{curr_loc_fips = as.character(curr_loc_fips)}

# condition = expression((location==curr_loc_fips))

all_props <- c()
all_CI_widths <- 1-c(0.02,0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9)

for (j in 1:length(all_CI_widths)){
  curr_a_level = round(1 - all_CI_widths[j],3)
  # curr_prop <- get_calibration_prop(forecasts_with_truth,curr_a_level,
  #                                   expression((location == curr_loc_fips)),"case")
  curr_prop <- get_calibration_prop(forecasts_with_truth,curr_a_level,
                                    condition,tar_type)
  all_props <- c(all_props,curr_prop)
}


calib_df <- data.frame(exp_prop = all_CI_widths,act_prop = all_props)


figfilename_calibration <- sprintf("results_figures/US_%swkaheaddeaths_calibration.pdf",i)

pdf(figfilename_calibration, width = 5, height = 3)
a3 <- ggplot(data=calib_df)+
  geom_point(mapping=aes(x=exp_prop,y=act_prop))+
  geom_abline(slope=1,intercept=0)+
  xlim(c(0,1))+ylim(c(0,1))+
  xlab("Expected Proportion")+
  ylab("Actual Proportion")+
  ggtitle(title_text)
print(a3)
dev.off()
}

for (i in 1:4){



cond_target = sprintf("%s wk ahead cum death",i)
condition = expression((target==cond_target)&(location!="US"))
tar_type = "death"
# title_text <- paste(curr_loc,"-",tar_type)
title_text <- sprintf("State-level - %s week ahead cumulative deaths",i)



all_props <- c()
all_CI_widths <- 1-c(0.02,0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9)

for (j in 1:length(all_CI_widths)){
  curr_a_level = round(1 - all_CI_widths[j],3)
  # curr_prop <- get_calibration_prop(forecasts_with_truth,curr_a_level,
  #                                   expression((location == curr_loc_fips)),"case")
  curr_prop <- get_calibration_prop(forecasts_with_truth,curr_a_level,
                                    condition,tar_type)
  all_props <- c(all_props,curr_prop)
}


calib_df <- data.frame(exp_prop = all_CI_widths,act_prop = all_props)


figfilename_calibration <- sprintf("results_figures/state-level_%swkaheaddeaths_calibration.pdf",i)

pdf(figfilename_calibration, width = 5, height = 3)
a3 <- ggplot(data=calib_df)+
  geom_point(mapping=aes(x=exp_prop,y=act_prop))+
  geom_abline(slope=1,intercept=0)+
  xlim(c(0,1))+ylim(c(0,1))+
  xlab("Expected Proportion")+
  ylab("Actual Proportion")+
  ggtitle(title_text)
print(a3)
dev.off()
}


calib_df <- data.frame()

for (i in 1:4){



cond_target = sprintf("%s wk ahead cum death",i)
condition1 = expression((target==cond_target)&(location!="US"))
condition2 = expression((target==cond_target)&(location=="US"))
tar_type = "death"
# title_text <- paste(curr_loc,"-",tar_type)
title_text <- sprintf("%s week ahead",i)



all_props1 <- c()
all_props2 <- c()
all_CI_widths <- 1-c(0.02,0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9)

for (j in 1:length(all_CI_widths)){
  curr_a_level = round(1 - all_CI_widths[j],3)
  # curr_prop <- get_calibration_prop(forecasts_with_truth,curr_a_level,
  #                                   expression((location == curr_loc_fips)),"case")
  curr_prop1 <- get_calibration_prop(forecasts_with_truth,curr_a_level,
                                    condition1,tar_type)
  all_props1 <- c(all_props1,curr_prop1)
  
  curr_prop2 <- get_calibration_prop(forecasts_with_truth,curr_a_level,
                                    condition2,tar_type)
  all_props2 <- c(all_props2,curr_prop2)
}


temp1 <- data.frame(exp_prop = all_CI_widths,act_prop = all_props1,level="state",target=title_text)
temp2 <- data.frame(exp_prop = all_CI_widths,act_prop = all_props2,level="US",target=title_text)
calib_df <- rbind(calib_df,temp1,temp2)

}

figfilename_calibration <- sprintf("results_figures/deaths_calibration.pdf",i)

pdf(figfilename_calibration, width = 7, height = 4)
a3 <- ggplot(data=calib_df)+
  geom_point(mapping=aes(x=exp_prop,y=act_prop,shape=level,color=level),cex=1.5)+
  # geom_point(mapping=aes(x=exp_prop,y=us_prop,shape="b"),shape=19)+
  geom_abline(slope=1,intercept=0)+
  xlim(c(0,1))+ylim(c(0,1))+
  xlab("Expected Proportion")+
  ylab("Actual Proportion")+
  ggtitle("PI Capture Rates for Cumulative Death Forecasts")+
  facet_wrap(facets=vars(target),nrow=2)

print(a3)
dev.off()
null device 
          1 
print(a3)




calib_df <- data.frame()

for (i in 1:4){



cond_target = sprintf("%s wk ahead cum case",i)
condition1 = expression((target==cond_target)&(location!="US"))
condition2 = expression((target==cond_target)&(location=="US"))
tar_type = "case"
# title_text <- paste(curr_loc,"-",tar_type)
title_text <- sprintf("%s week ahead",i)



all_props1 <- c()
all_props2 <- c()
all_CI_widths <- 1-c(0.02,0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9)

for (j in 1:length(all_CI_widths)){
  curr_a_level = round(1 - all_CI_widths[j],3)
  # curr_prop <- get_calibration_prop(forecasts_with_truth,curr_a_level,
  #                                   expression((location == curr_loc_fips)),"case")
  curr_prop1 <- get_calibration_prop(forecasts_with_truth,curr_a_level,
                                    condition1,tar_type)
  all_props1 <- c(all_props1,curr_prop1)
  
  curr_prop2 <- get_calibration_prop(forecasts_with_truth,curr_a_level,
                                    condition2,tar_type)
  all_props2 <- c(all_props2,curr_prop2)
}


temp1 <- data.frame(exp_prop = all_CI_widths,act_prop = all_props1,level="state",target=title_text)
temp2 <- data.frame(exp_prop = all_CI_widths,act_prop = all_props2,level="US",target=title_text)
calib_df <- rbind(calib_df,temp1,temp2)

}

figfilename_calibration <- sprintf("results_figures/cases_calibration.pdf",i)

pdf(figfilename_calibration, width = 7, height = 4)
a3 <- ggplot(data=calib_df)+
  geom_point(mapping=aes(x=exp_prop,y=act_prop,shape=level,color=level),cex=1.5)+
  # geom_point(mapping=aes(x=exp_prop,y=us_prop,shape="b"),shape=19)+
  geom_abline(slope=1,intercept=0)+
  xlim(c(0,1))+ylim(c(0,1))+
  xlab("Expected Proportion")+
  ylab("Actual Proportion")+
  ggtitle("PI Capture Rates for Cumulative Case Forecasts")+
  facet_wrap(facets=vars(target),nrow=2)

print(a3)
dev.off()
null device 
          1 
print(a3)

LS0tCnRpdGxlOiAiRXBpQ292REEgRmlndXJlcyIKb3V0cHV0OgogIHBkZl9kb2N1bWVudDogCiAgICBrZWVwX3RleDogdHJ1ZQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKLS0tCgpDcmVhdGVkIDIvMjIvMjEgXCwgSFJCCgpVcGRhdGVkIDMvOS8yMSBcLCBTd2l0Y2hlZCBDT1ZJRCBUcmFja2luZyBQcm9qZWN0IFRydXRoIHRvIGRvd25sb2FkIGZyb20gMTEvMTYvMjAyMAoKVXBkYXRlZCA0LzExLzIxIFwsIFVzZSByZXN1bHRzIHdoaWNoIGluY2x1ZGUgUFIKCgojIyBSZWFkIGluIGZvcmVjYXN0cyBhbmQgYXR0YWNoIHRydXRoIAoKYGBge3Igc2V0dXAsIGVjaG89RkFMU0Usd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQoKIyBnZXR3ZCgpCgpsaWJyYXJ5KCJjZGxUb29scyIscXVpZXRseSA9IFRSVUUpICMgcGFja2FnZSB0byBjb252ZXJ0IGZpcHMgdG8gc3RhdGUgYWJicmV2aWF0aW9ucwpsaWJyYXJ5KGdnbmV3c2NhbGUscXVpZXRseSA9IFRSVUUpCmxpYnJhcnkodGlkeXZlcnNlLHF1aWV0bHkgPSBUUlVFLHdhcm4uY29uZmxpY3RzPUZBTFNFKQpsaWJyYXJ5KGNvdmlkY2FzdCxxdWlldGx5ID0gVFJVRSkKbGlicmFyeShNTVdSd2VlayxxdWlldGx5ID0gVFJVRSkKbGlicmFyeShrbml0ciwgcXVpZXRseSA9IFRSVUUpCmxpYnJhcnkoZ2dwbG90MixxdWlldGx5ID0gVFJVRSkKCgojIEVwaUNvdkRBIEZvcmVjYXN0cyAtIENUUApmb3JlY2FzdHNfd2l0aF90cnV0aCA9IHJlYWQuY3N2KCJVQS1FcGlDb3ZEQS12My1hbHBoYURhdGEtdHViZTEtcHJpb3ItMjAyMC0xMS0xNi1QQVBFUi9mb3JlY2FzdHNfd2l0aF90cnV0aC1VQS1FcGlDb3ZEQS1DVFAuY3N2IikKRXBpQ292REFfcHRfc2NvcmVzID0gcmVhZC5jc3YoIlVBLUVwaUNvdkRBLXYzLWFscGhhRGF0YS10dWJlMS1wcmlvci0yMDIwLTExLTE2LVBBUEVSL0NUUF9hbGxfc2NvcmVzLmNzdiIpCgpFcGlDb3ZEQV9wdF9zY29yZXMgPSBzdWJzZXQoRXBpQ292REFfcHRfc2NvcmVzLGFzLkRhdGUoZm9yZWNhc3RfZGF0ZSk+PSBhcy5EYXRlKCIyMDIwLTA1LTAxIikpCmZvcmVjYXN0c193aXRoX3RydXRoID0gc3Vic2V0KGZvcmVjYXN0c193aXRoX3RydXRoLGFzLkRhdGUoZm9yZWNhc3RfZGF0ZSk+PSBhcy5EYXRlKCIyMDIwLTA1LTAxIikpCgoKYGBgCgoKYGBge3J9Cm15ZmlwczwtZnVuY3Rpb24oZmlwc19jb2Rlcyx0bz0iQWJicmV2aWF0aW9uIil7CiAgZmlwc19hYmJyZXYgPC0gZmlwc19jb2RlcwogIGZpcHNfYWJicmV2W2ZpcHNfY29kZXMhPSJVUyJdIDwtIGZpcHMoZmlwc19jb2Rlc1tmaXBzX2NvZGVzIT0iVVMiXSx0bz0iQWJicmV2aWF0aW9uIikgCiAgZmlwc19hYmJyZXZbZmlwc19jb2Rlcz09IlVTIl0gPC0gIiBVUyIKICAKICByZXR1cm4oZmlwc19hYmJyZXYpCiAgCn0KYGBgCgoKCiMjIENhbGN1bGF0ZSBpbnRlcnZhbCBzY29yZXMgZm9yIGEgc3BlY2lmaWVkIGxldmVsIChhbHBoYSkKCiQkIFx0ZXh0e0lTfV97XGFscGhhfShGLHkpID0gKHUgLSBcZWxsKSArIFxmcmFjezJ9e1xhbHBoYX0gKFxlbGwgLSB5KSBcbWF0aGJmezF9KFxlbGwgPiB5KSArIFxmcmFjezJ9e1xhbHBoYX0oeSAtIHUpXG1hdGhiZnsxfSh1IDwgeSkkJAoKCmBgYHtyIGRlZmluZS1nZXRJbnRlcnZhbFNjb3Jlcy1mdW5jdGlvbiwgZWNobz1GQUxTRX0KZ2V0SW50ZXJ2YWxTY29yZXMgPC0gZnVuY3Rpb24oZm9yZWNhc3RzX3dpdGhfdHJ1dGgsYWxwaGFfbGV2ZWwpewoKICB1cHBlcmJvdW5kcyA9IHN1YnNldChmb3JlY2FzdHNfd2l0aF90cnV0aCxxdWFudGlsZSA9PSgxLWFscGhhX2xldmVsLzIpLAogICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdD0idmFsdWUiICkKICAKICBsb3dlcmJvdW5kcyA9IHN1YnNldChmb3JlY2FzdHNfd2l0aF90cnV0aCxxdWFudGlsZSA9PShhbHBoYV9sZXZlbC8yKSwKICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3Q9InZhbHVlIiApCiAgCiAgdHJ1dGhzID0gIHN1YnNldChmb3JlY2FzdHNfd2l0aF90cnV0aCxxdWFudGlsZSA9PShhbHBoYV9sZXZlbC8yKSwKICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3Q9InRydXRoIiApCiAgCiAgaW50ZXJ2YWxfc2NvcmVzID0gKHVwcGVyYm91bmRzLWxvd2VyYm91bmRzKSArIAogICAgICAgICAgICAgICAgICAgIDIvYWxwaGFfbGV2ZWwgKiAobG93ZXJib3VuZHMtdHJ1dGhzKSoKICAgICAgICAgICAgICAgICAgICAgICAgICAobG93ZXJib3VuZHM+dHJ1dGhzKSArIAogICAgICAgICAgICAgICAgICAgIDIvYWxwaGFfbGV2ZWwgKiAodHJ1dGhzLXVwcGVyYm91bmRzKSoodXBwZXJib3VuZHM8dHJ1dGhzKQogIAogIElTX2RmID0gc3Vic2V0KGZvcmVjYXN0c193aXRoX3RydXRoLHF1YW50aWxlID09KGFscGhhX2xldmVsLzIpLAogICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdD1jKCJmb3JlY2FzdF9kYXRlIiwidGFyZ2V0IiwidGFyZ2V0X2VuZF9kYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRpb24iLCJ0cnV0aCIpICkKICAKICBJU19kZiRpbnRlcnZhbF9zY29yZSA9IGludGVydmFsX3Njb3JlcyR2YWx1ZQogIAogIHJldHVybihJU19kZikKfQpgYGAKCgoKCiMjIENhbGN1bGF0ZSBXZWlnaHRlZCBJbnRlcnZhbCBTY29yZSAoV0lTKQoKJCQgXHRleHR7V0lTfV97XGFscGhhXzAgOiBLfShGLHkpID0gXGZyYWN7MX17SysxLzJ9XGxlZnQod18wIFwsIHx5LW18ICsgXHN1bV97az0xfV5LIHdfayBcLCBcdGV4dHtJU31fe1xhbHBoYV9rfShGLHkpXHJpZ2h0KSAkJAp3aGVyZSAkd18wID0gXGZyYWMgMSAyJCBhbmQgJHdfayA9IFxmcmFje1xhbHBoYV9rfXsyfSQgZm9yICRrID0gMSxcaGRvdHMsIEskIGZvciAkXGFscGhhPSAwLjAyLCAwLjA1LCAwLjEsIDAuMiwgXGhkb3RzLCAwLjkkLiAgCgoKCmBgYHtyIGRlZmluZS1nZXRXSVMtZnVuY3Rpb259CmdldFdJUyA8LSBmdW5jdGlvbihmb3JlY2FzdHNfd2l0aF90cnV0aCl7CgoKICAKICB3MCA9IDAuNQogIGZfbWVkaWFucyA9ICBzdWJzZXQoZm9yZWNhc3RzX3dpdGhfdHJ1dGgscXVhbnRpbGUgPT0wLjUsCiAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0PSJ2YWx1ZSIgKQogIHRydXRoX3kgPSBzdWJzZXQoZm9yZWNhc3RzX3dpdGhfdHJ1dGgscXVhbnRpbGUgPT0wLjUsCiAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0PSJ0cnV0aCIgKQogIFdJUyA8LSB3MCphYnModHJ1dGhfeS1mX21lZGlhbnMpCiAgCiAgZm9yIChhbHBoYV9sZXZlbCBpbiBjKDAuMDIsMC4wNSwwLjEsMC4yLDAuMywwLjQsMC41LDAuNiwwLjcsMC44LDAuOSkpewogICAgd2sgPSBhbHBoYV9sZXZlbC8yCiAgICB0ZW1wX0lTIDwtIGdldEludGVydmFsU2NvcmVzKGZvcmVjYXN0c193aXRoX3RydXRoLGFscGhhX2xldmVsKQogICAgCiAgICBXSVMgPC0gV0lTICsgd2sqdGVtcF9JUyRpbnRlcnZhbF9zY29yZQogICAgCiAgfQogIAogICAgV0lTX3RlbXBfZGYgPC0gc3Vic2V0KGZvcmVjYXN0c193aXRoX3RydXRoLHF1YW50aWxlID09MC41LAogICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdD1jKCJmb3JlY2FzdF9kYXRlIiwidGFyZ2V0IiwidGFyZ2V0X2VuZF9kYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRpb24iLCJ0cnV0aCIpICkKICAgIFdJU190ZW1wX2RmJFdJUyA8LSBXSVMkdHJ1dGgKICAKICAKIAogIAogIHJldHVybihXSVNfdGVtcF9kZikKfQpgYGAKCgoKCmBgYHtyIGNhbGN1bGF0ZS1tb2RlbC1XSVN9CkVwaUNvdkRBX1dJUyA8LSBnZXRXSVMoZm9yZWNhc3RzX3dpdGhfdHJ1dGgpCgpFcGlDb3ZEQV9XSVMkSVNfMDUgPC0gZ2V0SW50ZXJ2YWxTY29yZXMoZm9yZWNhc3RzX3dpdGhfdHJ1dGgsIDAuMDUpJGludGVydmFsX3Njb3JlCgpFcGlDb3ZEQV9XSVMkSVNfNTAgPC0gZ2V0SW50ZXJ2YWxTY29yZXMoZm9yZWNhc3RzX3dpdGhfdHJ1dGgsIDAuNSkkaW50ZXJ2YWxfc2NvcmUKCmBgYAoKCiMgQXR0YWNoIHN0YXRlIHBvcHVsYXRpb24KCgpgYGB7cn0KRXBpQ292REFfV0lTJHBvcHVsYXRpb24gPC0gTkEKRXBpQ292REFfcHRfc2NvcmVzJHBvcHVsYXRpb24gPC0gTkEKCgoKZm9yIChsb2MgaW4gdW5pcXVlKEVwaUNvdkRBX1dJUyRsb2NhdGlvbikpewogIGlmIChsb2MgPT0gIlVTIil7CiAgICB0ZW1wX3BvcCA8LSBhcy5udW1lcmljKHJlYWQuY3N2KCIuLi9zdGF0ZV9ob3NwX2RhdGFfMjAyMC0xMS0xNi9zdGF0ZV9wb3BVUy5jc3YiKSkKICAgIEVwaUNvdkRBX1dJU1t3aGljaChFcGlDb3ZEQV9XSVMkbG9jYXRpb249PSJVUyIpLCJwb3B1bGF0aW9uIl0gPC0gdGVtcF9wb3AKICAgIEVwaUNvdkRBX3B0X3Njb3Jlc1t3aGljaChFcGlDb3ZEQV9wdF9zY29yZXMkbG9jYXRpb24gPT0gIlVTIiksInBvcHVsYXRpb24iXSA8LSB0ZW1wX3BvcAoKICAgIAogIH0gZWxzZSB7CgogICAgICAgIHRlbXBfcG9wIDwtIGFzLm51bWVyaWMocmVhZC5jc3Yoc3ByaW50ZigiLi4vc3RhdGVfaG9zcF9kYXRhXzIwMjAtMTEtMTYvc3RhdGVfcG9wJXMuY3N2IixmaXBzKGxvYywgdG8gPSAiQWJicmV2aWF0aW9uIikpKSkKICAgIEVwaUNvdkRBX1dJU1t3aGljaChFcGlDb3ZEQV9XSVMkbG9jYXRpb249PWxvYyksInBvcHVsYXRpb24iXSA8LSB0ZW1wX3BvcAogICAgRXBpQ292REFfcHRfc2NvcmVzW3doaWNoKEVwaUNvdkRBX3B0X3Njb3JlcyRsb2NhdGlvbiA9PSBmaXBzKGxvYywgdG8gPSAiQWJicmV2aWF0aW9uIikpLCJwb3B1bGF0aW9uIl0gPC0gdGVtcF9wb3AKCiAgCiAgfQogIAogIAp9CgoKCgpgYGAKCgoKCmBgYHtyIGRlZmluZS1zdGF0c09uUGVyZm9ybWFuY2UtZnVuY3Rpb259CnN0YXRzT25QZXJmb3JtYW5jZSA8LSBmdW5jdGlvbihhbGxfSVMsY29sdW1uLHRhcl90eXBlID0gImRlYXRoIixmaWx0ZXJkYXRlc19URiA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlVVMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFVTb25seSA9IEZBTFNFKXsKICAKICBhbGxfdGFyZ2V0cyA8LSBjKCIxIHdrIGFoZWFkIGN1bSBkZWF0aCIsIjIgd2sgYWhlYWQgY3VtIGRlYXRoIiwiMyB3ayBhaGVhZCBjdW0gZGVhdGgiLAogICAgICAgICAgICAgICAgICAgIjQgd2sgYWhlYWQgY3VtIGRlYXRoIikKICAKICBpZiAodGFyX3R5cGUgPT0gImNhc2UiKXsKICAgIGFsbF90YXJnZXRzIDwtIGMoIjEgd2sgYWhlYWQgY3VtIGNhc2UiLCIyIHdrIGFoZWFkIGN1bSBjYXNlIiwKICAgICAgICAgICAgICAgICAgICAgIjMgd2sgYWhlYWQgY3VtIGNhc2UiLAogICAgICAgICAgICAgICAgICAgICAiNCB3ayBhaGVhZCBjdW0gY2FzZSIpCiAgfQogIAogIAogIAogIGlmIChmaWx0ZXJkYXRlc19URil7CiAgICBhbGxfSVMgPC0gc3Vic2V0KGFsbF9JUywoYXMuRGF0ZShmb3JlY2FzdF9kYXRlKT5hcy5EYXRlKCIyMDIwLTA0LTE5IikpKQogICAgCiAgfQogIAogIGlmICghaW5jbHVkZVVTKXsKICAgIHN1YnNldChhbGxfSVMsKGxvY2F0aW9uIT0iVVMiKSkKICB9CiAgCiAgaWYgKFVTb25seSl7CiAgICBhbGxfSVMgPC0gc3Vic2V0KGFsbF9JUywobG9jYXRpb249PSJVUyIpKQogIH0KCgogIAogIHN0YXRzX2RmIDwtIGRhdGEuZnJhbWUoKQogIAogIHRlbXBfZGYgPC0gZGF0YS5mcmFtZSh0YXJnZXQgPSAiT3ZlcmFsbCIsbWVhbiA9IG1lYW4oYWxsX0lTW2dyZXBsKHRhcl90eXBlLGFsbF9JUyR0YXJnZXQpLGNvbHVtbl0pLAogICAgICAgICAgICAgICAgICAgICAgICBtZWRpYW4gPSBtZWRpYW4oYWxsX0lTW2dyZXBsKHRhcl90eXBlLGFsbF9JUyR0YXJnZXQpLGNvbHVtbl0pKQogIAogIHN0YXRzX2RmIDwtIHJiaW5kKHN0YXRzX2RmLHRlbXBfZGYpCiAgCiAgCiAgCiAgZm9yICh0YXIgaW4gYWxsX3RhcmdldHMpewogICAgY3Vycl9zdWJzZXQgPC0gc3Vic2V0KGFsbF9JUyx0YXJnZXQgPT0gdGFyKQogICAgY3Vycl9tZWFuIDwtIG1lYW4oY3Vycl9zdWJzZXRbLGNvbHVtbl0pCiAgICBjdXJyX21lZGlhbiA8LSBtZWRpYW4oY3Vycl9zdWJzZXRbLGNvbHVtbl0pCiAgICB0ZW1wX2RmIDwtIGRhdGEuZnJhbWUodGFyZ2V0ID0gdGFyLCBtZWFuID0gY3Vycl9tZWFuLG1lZGlhbiA9IGN1cnJfbWVkaWFuKQogICAgCiAgICBzdGF0c19kZiA8LSByYmluZChzdGF0c19kZix0ZW1wX2RmKQogIH0KICAKICByZXR1cm4oc3RhdHNfZGYpCn0KYGBgCgoKCgoKYGBge3J9CklTX3N0YXRzX0VwaUNvdkRBIDwtIHN0YXRzT25QZXJmb3JtYW5jZShFcGlDb3ZEQV9XSVMsIldJUyIpCmBgYAoKCgpgYGB7cn0KIyBrYWJsZShJU19zdGF0c19FcGlDb3ZEQSxib29rdGFicz1UUlVFKQojIGthYmxlKElTX3N0YXRzX0NPVklEaHViLGJvb2t0YWJscz1UUlVFKQpgYGAKCgoKIyBQb2ludCBTY29yZXMKCgpgYGB7cn0KRXBpQ292REFfcHRfc2NvcmVzJGFic19lcnJvciA8LSBhYnMoRXBpQ292REFfcHRfc2NvcmVzJGVycm9yKQoKRXBpQ292REFfcHRfc3RhdHMgPC0gc3RhdHNPblBlcmZvcm1hbmNlKEVwaUNvdkRBX3B0X3Njb3JlcywiYWJzX2Vycm9yIikKCmBgYAoKCiMgUGxvdCBJU19hbHBoYQoKYGBge3J9CgoKCmdncGxvdCgpKwogIGdlb21fbGluZShkYXRhPUVwaUNvdkRBX1dJU1soRXBpQ292REFfV0lTJHRhcmdldD09IjEgd2sgYWhlYWQgY3VtIGRlYXRoIikmKEVwaUNvdkRBX1dJUyRsb2NhdGlvbj09IlVTIiksXSwKICAgICAgICAgICAgbWFwcGluZz1hZXMoeD1hcy5EYXRlKGZvcmVjYXN0X2RhdGUpLHk9SVNfMDUsZ3JvdXA9bG9jYXRpb24pKQoKCgpnZ3Bsb3QoKSsKICAgIGdlb21fbGluZShkYXRhPXN1YnNldCgKICAgICAgICAgICAgICAgIEVwaUNvdkRBX3B0X3Njb3Jlc1tncmVwbCgiZGVhdGgiLEVwaUNvdkRBX3B0X3Njb3JlcyR0YXJnZXQpLF0sbG9jYXRpb249PSJVUyIpLAogICAgICAgICAgICBtYXBwaW5nPWFlcyh4PWFzLkRhdGUoZm9yZWNhc3RfZGF0ZSkseT1hYnNfZXJyb3IsZ3JvdXA9dGFyZ2V0LGNvbG9yPXRhcmdldCkpCgoKYGBgCgoKYGBge3J9CklTX2RlYXRoc19kZiA8LSBFcGlDb3ZEQV9XSVNbZ3JlcGwoImRlYXRoIixFcGlDb3ZEQV9XSVMkdGFyZ2V0KSxdCgoKdGhpc2ZpbGVuYW1lIDwtICJyZXN1bHRzX2ZpZ3VyZXMvVVNfSVMucGRmIgoKcGRmKHRoaXNmaWxlbmFtZSwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNikKYTMgPC0gZ2dwbG90KCkrCiAgZ2VvbV9saW5lKGRhdGE9SVNfZGVhdGhzX2RmWyhJU19kZWF0aHNfZGYkbG9jYXRpb249PSJVUyIpLF0sCiAgICAgICAgICAgIG1hcHBpbmc9YWVzKHg9YXMuRGF0ZShmb3JlY2FzdF9kYXRlKSx5PUlTXzA1LGdyb3VwPXRhcmdldCxjb2xvcj10YXJnZXQpKSsKICB4bGFiKCJGb3JlY2FzdCBEYXRlIikreWxhYigiSW50ZXJ2YWwgU2NvcmUiKStnZ3RpdGxlKCJJbnRlcnZhbCBTY29yZXMgZm9yIFVTIERlYXRoIEZvcmVjYXN0cyIpKwogICAgc2NhbGVfY29sb3JfZGlzY3JldGUobmFtZT0iVGFyZ2V0IixsYWJlbHMgPSBjKCIxIHdrIGFoZWFkIGNtbHR2IGRlYXRocyIsIjIgd2sgYWhlYWQgY21sdHYgZGVhdGhzIiwiMyB3ayBhaGVhZCBjbWx0diBkZWF0aHMiLCI0IHdrIGFoZWFkIGNtbHR2IGRlYXRocyIpKSsKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNikpCnByaW50KGEzKQpkZXYub2ZmKCkKCmBgYAoKCiMgRGVhdGggUmVzdWx0czogV2VlayBhaGVhZCBpbnRlcnZhbCBzY29yZSBmaWd1cmVzCgoKUGVyIDEwMCwwMDAgcG9wdWxhdGlvbgoKTWF4IElTXzA1L3BvcHVsYXRpb24qMTBeNSBpcyBhYm91dCA2NzcKCmBgYHtyIGRlYXRocy1JU30KCiMgZm9yIChpIGluIDE6NCl7CgoKbXlicmVha3MgPSBjKDAsMSw1LDEwLDI1LDUwLDEwMCw1MDAsSW5mKQpteWxhYmVscyA9IGMoIjwxIiwiMSA8PSB4IDwgNSIsICI1IDw9IHggPCAxMCIsICIxMCA8PSB4IDwgMjUiLCIyNSA8PSB4IDwgNTAiLCI1MCA8PSB4IDwgMTAwIiwgIjEwMCA8PSB4IDwgNTAwIiwiPj01MDAiKQoKZmlnZmlsZW5hbWUzIDwtIHNwcmludGYoInJlc3VsdHNfZmlndXJlcy9JU19lcGljb3ZkYV9kZWF0aHMucGRmIikKCnBkZihmaWdmaWxlbmFtZTMsIHdpZHRoID0gMTAsIGhlaWdodCA9IDcpCnAxIDwtIGdncGxvdCgpKwogIGdlb21fdGlsZShkYXRhID0gSVNfZGVhdGhzX2RmLAogICAgICAgICAgICBtYXBwaW5nID0KICAgICAgICAgICAgICBhZXMoYXMuRGF0ZShmb3JlY2FzdF9kYXRlKSwKICAgICAgICAgICAgICAgICAgbXlmaXBzKGFzLmNoYXJhY3Rlcihsb2NhdGlvbiksdG89IkFiYnJldmlhdGlvbiIpLAogICAgICAgICAgICAgICAgICBmaWxsPWN1dChJU18wNS9wb3B1bGF0aW9uKjEwXjUsYnJlYWtzPW15YnJlYWtzLHJpZ2h0PUZBTFNFLGxhYmVscz1teWxhYmVscykpKSArCiAgIyBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAid2hpdGUiLGhpZ2g9ImZpcmVicmljazMiLG1pZD0iZ29sZCIsdHJhbnM9ImxvZzEwIixtaWRwb2ludD0xLAogICMgICAgICAgICAgICAgICAgICAgICBicmVha3M9bXlicmVha3MsbGFiZWxzPW15YnJlYWtzKSsKICBzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJzZXEiLHBhbGV0dGUgPSAiWWxPclJkIikrCiAgdGhlbWVfYncoKSsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSsKICB4bGFiKCJGb3JlY2FzdCBEYXRlIikreWxhYigiU3RhdGUiKSsKICBnZ3RpdGxlKCJFcGlDb3ZEQSBJbnRlcnZhbCBTY29yZXMgKGFscGhhPTAuMDUpIGZvciBEZWF0aCBGb3JlY2FzdHMiKSsKICAjIGdndGl0bGUoc3ByaW50ZigiRXBpQ292REEsICVzIHdrIGFoZWFkIGN1bXVsYXRpdmUgZGVhdGhzIixpKSkrCiAgbGFicyhmaWxsPSJJUyBwZXIgMTAwLDAwMCIpKwogIGZhY2V0X3dyYXAodmFycyh0YXJnZXQpLHNjYWxlcz0iZml4ZWQiLG5yb3c9MSkKcHJpbnQocDEpCmRldi5vZmYoKQpwcmludChwMSkKCiMgfQoKIyBtYXgoSVNfZGVhdGhzX2RmJElTXzA1L0lTX2RlYXRoc19kZiRwb3B1bGF0aW9uKjEwXjUpCgoKYGBgCgoKIyBQb2ludCBFc3RpbWF0ZSBFcnJvcnMKCk1heCBjdW11bGF0aXZlIGRlYXRocyBmb3IgNCB3ZWVrIGFoZWFkIGZvcmVjYXN0czogYWJvdXQgMjguOSBwZXIgMTAwLDAwMAoKYGBge3IgQUUtZGVhdGhzMX0KCm15YnJlYWtzMiA8LSBjKDAsLjUsMSw1LDEwLDE1LDI1LEluZikKbXlsYWJlbHMyID0gYygiPDAuNSIsIjAuNSA8PSB4IDwgMSIsIjEgPD0geCA8IDUiLCAiNSA8PSB4IDwgMTAiLCAiMTAgPD0geCA8IDE1IiwiMTUgPD0geCA8IDI1IiwiPj0yNSIpCgoKCmZpZ2ZpbGVuYW1lMSA8LSBzcHJpbnRmKCJyZXN1bHRzX2ZpZ3VyZXMvQUVfZXBpY292ZGFfZGVhdGhzLnBkZiIpCgoKCgpwZGYoZmlnZmlsZW5hbWUxLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA3KQpwMzwtIGdncGxvdCgpKwogIGdlb21fdGlsZShkYXRhID0gRXBpQ292REFfcHRfc2NvcmVzW2dyZXBsKCJkZWF0aCIsRXBpQ292REFfcHRfc2NvcmVzJHRhcmdldCksXSwKICAgICAgICAgICAgbWFwcGluZyA9CiAgICAgICAgICAgICAgYWVzKGFzLkRhdGUoZm9yZWNhc3RfZGF0ZSksCiAgICAgICAgICAgICAgICAgIG15Zmlwcyhhcy5jaGFyYWN0ZXIobG9jYXRpb24pLHRvPSJBYmJyZXZpYXRpb24iKSwKICAgICAgICAgICAgICAgICAgZmlsbD0gICBjdXQoYWJzX2Vycm9yL3BvcHVsYXRpb24qMTBeNSxicmVha3M9bXlicmVha3MyLHJpZ2h0PUZBTFNFLGxhYmVscz1teWxhYmVsczIpICkpICsKICAjIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGxvdyA9ICJ3aGl0ZSIsbWlkPSJnb2xkIixoaWdoPSJmaXJlYnJpY2szIiwKICAjICAgICAgICAgICAgICAgICAgICAgIHRyYW5zPSJsb2cxMCIsbWlkcG9pbnQgPSBsb2cxMCguNSksbmEudmFsdWUgPSAid2hpdGUiLGJyZWFrcz1teWJyZWFrczIsCiAgIyAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9bXlicmVha3MyKSsKICBzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJzZXEiLHBhbGV0dGUgPSAiWWxPclJkIikrCiAgdGhlbWVfYncoKSsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSsKICB4bGFiKCJGb3JlY2FzdCBEYXRlIikreWxhYigiU3RhdGUiKStnZ3RpdGxlKCJFcGlDb3ZEQSBBYnNvbHV0ZSBFcnJvciBwZXIgMTAwLDAwMCBmb3IgRGVhdGggRm9yZWNhc3RzIikgKwogIGxhYnMoZmlsbD0iQWJzLiBFcnJvciBwZXIgMTAwLDAwMCIpKwogIGZhY2V0X3dyYXAodmFycyh0YXJnZXQpLHNjYWxlcz0iZml4ZWQiLG5yb3c9MSkKcHJpbnQocDMpCmRldi5vZmYoKQpwcmludChwMykKCgoKCiMgbWF4KEVwaUNvdkRBX3B0X3Njb3Jlc1soRXBpQ292REFfcHRfc2NvcmVzJHRhcmdldD09c3ByaW50ZigiJXMgd2sgYWhlYWQgY3VtIGRlYXRoIixqKSksImFic19lcnJvciJdL0VwaUNvdkRBX3B0X3Njb3Jlc1soRXBpQ292REFfcHRfc2NvcmVzJHRhcmdldD09c3ByaW50ZigiJXMgd2sgYWhlYWQgY3VtIGRlYXRoIixqKSksInBvcHVsYXRpb24iXSoxMF41KQpgYGAKCgoKIyMgIkhlYXRtYXBzIiBmb3IgQ2FzZXMgCgptYXggSVMgZm9yIGNhc2VzIGFib3V0IDIzNzUyCm1lZGlhbnQgSVMgZm9yIGNhc2VzIGFib3V0IDE5MwoKYGBge3IgY2FzZXMtSVN9CgojIGZvciAoaSBpbiAxOjQpewoKCm15YnJlYWtzID0gYygwLDUsMjUsMTAwLDMwMCwxMDAwLDUwMDAsMTAwMDAsSW5mKQpteWxhYmVscyA9IGMoIjw1IiwiNSA8PSB4IDwgMjUiLCAiMjUgPD0geCA8IDEwMCIsICIxMDAgPD0geCA8IDMwMCIsIjMwMCA8PSB4IDwgMTAwMCIsIjEwMDAgPD0geCA8IDUwMDAiLCAiNTAwMCA8PSB4IDwgMTAwMDAiLCI+PTEwMDAwIikKCmZpZ2ZpbGVuYW1lMyA8LSBzcHJpbnRmKCJyZXN1bHRzX2ZpZ3VyZXMvSVNfZXBpY292ZGFfY2FzZXMucGRmIikKCnBkZihmaWdmaWxlbmFtZTMsIHdpZHRoID0gMTAsIGhlaWdodCA9IDcpCnAxIDwtIGdncGxvdCgpKwogIGdlb21fdGlsZShkYXRhID0gRXBpQ292REFfV0lTW2dyZXBsKCJjYXNlIixFcGlDb3ZEQV9XSVMkdGFyZ2V0KSxdLAogICAgICAgICAgICBtYXBwaW5nID0KICAgICAgICAgICAgICBhZXMoYXMuRGF0ZShmb3JlY2FzdF9kYXRlKSwKICAgICAgICAgICAgICAgICAgbXlmaXBzKGFzLmNoYXJhY3Rlcihsb2NhdGlvbiksdG89IkFiYnJldmlhdGlvbiIpLAogICAgICAgICAgICAgICAgICBmaWxsPWN1dChJU18wNS9wb3B1bGF0aW9uKjEwXjUsYnJlYWtzPW15YnJlYWtzLHJpZ2h0PUZBTFNFLGxhYmVscz1teWxhYmVscykpKSArCiAgIyBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAid2hpdGUiLGhpZ2g9ImZpcmVicmljazMiLG1pZD0iZ29sZCIsdHJhbnM9ImxvZzEwIixtaWRwb2ludD0xLAogICMgICAgICAgICAgICAgICAgICAgICBicmVha3M9bXlicmVha3MsbGFiZWxzPW15YnJlYWtzKSsKICBzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJzZXEiLHBhbGV0dGUgPSAiWWxPclJkIikrCiAgdGhlbWVfYncoKSsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSsKICB4bGFiKCJGb3JlY2FzdCBEYXRlIikreWxhYigiU3RhdGUiKSsKICBnZ3RpdGxlKCJFcGlDb3ZEQSBJbnRlcnZhbCBTY29yZXMgKGFscGhhPTAuMDUpIGZvciBDYXNlIEZvcmVjYXN0cyIpKwogICMgZ2d0aXRsZShzcHJpbnRmKCJFcGlDb3ZEQSwgJXMgd2sgYWhlYWQgY3VtdWxhdGl2ZSBkZWF0aHMiLGkpKSsKICBsYWJzKGZpbGw9IklTIHBlciAxMDAsMDAwIikrCiAgZmFjZXRfd3JhcCh2YXJzKHRhcmdldCksc2NhbGVzPSJmaXhlZCIsbnJvdz0xKQpwcmludChwMSkKZGV2Lm9mZigpCnByaW50KHAxKQoKIyB9CgojIG1lZGlhbihFcGlDb3ZEQV9XSVNbZ3JlcGwoImNhc2UiLEVwaUNvdkRBX1dJUyR0YXJnZXQpLCJJU18wNSJdL0VwaUNvdkRBX1dJU1tncmVwbCgiY2FzZSIsRXBpQ292REFfV0lTJHRhcmdldCksInBvcHVsYXRpb24iXSoxMF41KQoKCmBgYAoKCiMgUG9pbnQgRXN0aW1hdGUgRXJyb3JzCgpNYXggY3VtdWxhdGl2ZSBkZWF0aHMgZm9yIDQgd2VlayBhaGVhZCBmb3JlY2FzdHM6IGFib3V0IDg2NyBwZXIgMTAwLDAwMAptZWRpYW4gYWJvdXQgNjcKCmBgYHtyIEFFLWRlYXRoc30KCm15YnJlYWtzMiA8LSBjKDAsMSwxMCwyNSw1MCwxMDAsMzAwLDUwMCxJbmYpCm15bGFiZWxzMiA9IGMoIjwxIiwiMSA8PSB4IDwgMTAiLCIxMCA8PSB4IDwgMjUiLCAiMjUgPD0geCA8IDUwIiwgIjUwIDw9IHggPCAxMDAiLCIxMDAgPD0geCA8IDMwMCIsCiAgICAgICAgICAgICAgIjMwMCA8PSB4IDwgNTAwIiwiPj01MDAiKQoKCgpmaWdmaWxlbmFtZTEgPC0gc3ByaW50ZigicmVzdWx0c19maWd1cmVzL0FFX2VwaWNvdmRhX2Nhc2VzLnBkZiIpCgoKCgpwZGYoZmlnZmlsZW5hbWUxLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA3KQpwMzwtIGdncGxvdCgpKwogIGdlb21fdGlsZShkYXRhID0gRXBpQ292REFfcHRfc2NvcmVzW2dyZXBsKCJjYXNlIixFcGlDb3ZEQV9wdF9zY29yZXMkdGFyZ2V0KSxdLAogICAgICAgICAgICBtYXBwaW5nID0KICAgICAgICAgICAgICBhZXMoYXMuRGF0ZShmb3JlY2FzdF9kYXRlKSwKICAgICAgICAgICAgICAgICAgbXlmaXBzKGFzLmNoYXJhY3Rlcihsb2NhdGlvbiksdG89IkFiYnJldmlhdGlvbiIpLAogICAgICAgICAgICAgICAgICBmaWxsPSAgIGN1dChhYnNfZXJyb3IvcG9wdWxhdGlvbioxMF41LGJyZWFrcz1teWJyZWFrczIscmlnaHQ9RkFMU0UsbGFiZWxzPW15bGFiZWxzMikgKSkgKwogICMgc2NhbGVfZmlsbF9ncmFkaWVudDIobG93ID0gIndoaXRlIixtaWQ9ImdvbGQiLGhpZ2g9ImZpcmVicmljazMiLAogICMgICAgICAgICAgICAgICAgICAgICAgdHJhbnM9ImxvZzEwIixtaWRwb2ludCA9IGxvZzEwKC41KSxuYS52YWx1ZSA9ICJ3aGl0ZSIsYnJlYWtzPW15YnJlYWtzMiwKICAjICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1teWJyZWFrczIpKwogIHNjYWxlX2ZpbGxfYnJld2VyKHR5cGU9InNlcSIscGFsZXR0ZSA9ICJZbE9yUmQiKSsKICB0aGVtZV9idygpKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpKwogIHhsYWIoIkZvcmVjYXN0IERhdGUiKSt5bGFiKCJTdGF0ZSIpK2dndGl0bGUoIkVwaUNvdkRBIEFic29sdXRlIEVycm9yIHBlciAxMDAsMDAwIGZvciBDYXNlIEZvcmVjYXN0cyIpICsKICBsYWJzKGZpbGw9IkFicy4gRXJyb3IgcGVyIDEwMCwwMDAiKSsKICBmYWNldF93cmFwKHZhcnModGFyZ2V0KSxzY2FsZXM9ImZpeGVkIixucm93PTEpCnByaW50KHAzKQpkZXYub2ZmKCkKcHJpbnQocDMpCgoKCgojIG1lZGlhbihFcGlDb3ZEQV9wdF9zY29yZXNbKEVwaUNvdkRBX3B0X3Njb3JlcyR0YXJnZXQ9PXNwcmludGYoIiVzIHdrIGFoZWFkIGN1bSBjYXNlIiw0KSksImFic19lcnJvciJdL0VwaUNvdkRBX3B0X3Njb3Jlc1soRXBpQ292REFfcHRfc2NvcmVzJHRhcmdldD09c3ByaW50ZigiJXMgd2sgYWhlYWQgY3VtIGNhc2UiLDQpKSwicG9wdWxhdGlvbiJdKjEwXjUpCgpgYGAKCgoKCgoKCmBgYHtyfQoKZm9yZWNhc3RlZF9sb2NhdGlvbnMgPSB1bmlxdWUoZm9yZWNhc3RzX3dpdGhfdHJ1dGgkbG9jYXRpb24pCgp0YXJfdHlwZSA9ICJjYXNlIgoKZm9yIChsb2MgaW4gZm9yZWNhc3RlZF9sb2NhdGlvbnMpewoKc3RhdGVfbnVtID0gbG9jICMiMDQiCgp0YXJnZXRfZm9yZWNhc3RzX3dpdGhfdHJ1dGggPC0KICBmb3JlY2FzdHNfd2l0aF90cnV0aFtncmVwbCh0YXJfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JlY2FzdHNfd2l0aF90cnV0aCR0YXJnZXQpICYKICAgICAgICAgICAgICAgICAgICAgICAgIChmb3JlY2FzdHNfd2l0aF90cnV0aCRsb2NhdGlvbiA9PSBzdGF0ZV9udW0pJgogICAgICAgICAgICAgICAgICAgICAgICAgYXMuRGF0ZShmb3JlY2FzdHNfd2l0aF90cnV0aCRmb3JlY2FzdF9kYXRlKQogICAgICAgICAgICAgICAgICAgICAgID5hcy5EYXRlKCcyMDIwLTA0LTEwJyksXQoKCnN0X2FiID0gZmlwcyhzdGF0ZV9udW0sdG89IkFiYnJldmlhdGlvbiIpCgppZiAobG9jID09ICJVUyIpewogIHN0X2FiID0gIlVTIgp9CmZjYXN0c193aWRlIDwtIGFzX3RpYmJsZSh0YXJnZXRfZm9yZWNhc3RzX3dpdGhfdHJ1dGgpICU+JQogICAgZmlsdGVyKHF1YW50aWxlICVpbiUgYygwLjAyNSwgMC4xLCAwLjI1LCAwLjUsIDAuNzUsIDAuOSwgMC45NzUpKSAlPiUKICAgIG11dGF0ZSh3ZWVrX2FoZWFkID0gYXMubnVtZXJpYyhzdWJzdHIodGFyZ2V0LCAwLDIpKSkgJT4lCiAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gcXVhbnRpbGUsIG5hbWVzX3ByZWZpeD0icSIpCgoKCmZpZ2ZpbGVuYW1lIDwtIHNwcmludGYoInJlc3VsdHNfZmlndXJlcy8lc18lc19xdWFudF9mb3JlY2FzdHMucGRmIixzdF9hYix0YXJfdHlwZSkKCnBkZihmaWdmaWxlbmFtZSwgd2lkdGggPSA1LCBoZWlnaHQgPSAzKQphMSA8LSBnZ3Bsb3QoZmNhc3RzX3dpZGUsYWVzKHggPSBhcy5EYXRlKHRhcmdldF9lbmRfZGF0ZSkpKSsKICAgIGdlb21fbGluZShhZXMoeT1xMC41LCBjb2xvcj1mb3JlY2FzdF9kYXRlLCBncm91cD1mb3JlY2FzdF9kYXRlKSkgKwogICAgIyBnZW9tX3JpYmJvbihhZXMoeW1pbj1xMC4xLCB5bWF4PXEwLjksIGZpbGw9Zm9yZWNhc3RfZGF0ZSwgZ3JvdXA9Zm9yZWNhc3RfZGF0ZSksIGFscGhhPS4zKSArCiAgICBnZW9tX3JpYmJvbihhZXMoeW1pbj1xMC4wMjUsIHltYXg9cTAuOTc1LCBmaWxsPWZvcmVjYXN0X2RhdGUsIGdyb3VwPWZvcmVjYXN0X2RhdGUpLCBhbHBoYT0uMykgKwogICAgZ2VvbV9yaWJib24oYWVzKHltaW49cTAuMjUsIHltYXg9cTAuNzUsIGZpbGw9Zm9yZWNhc3RfZGF0ZSwgZ3JvdXA9Zm9yZWNhc3RfZGF0ZSksIGFscGhhPS4zKSArCiAgICBnZW9tX3BvaW50KGFlcyh5PXRydXRoKSkgKwogICAgZ2VvbV9saW5lKGFlcyh5PXRydXRoKSkgKwogICAgdGhlbWVfYncoKSArIHhsYWIoIkRhdGUiKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsgeWxhYihzcHJpbnRmKCJjdW11bGF0aXZlICVzIixwYXN0ZTAodGFyX3R5cGUsInMiKSkpICsKICAgIGdndGl0bGUoc3ByaW50ZigiQ3VtdWxhdGl2ZSAlcyBpbiAlcywgb2JzZXJ2ZWQgYW5kIGZvcmVjYXN0ZWQiLHBhc3RlMCh0YXJfdHlwZSwicyIpLHN0X2FiKSkKIysKICAgICMgY29vcmRfY2FydGVzaWFuKHlsaW09YygyNTAsIDI1MDApKQoKcHJpbnQoYTEpCmRldi5vZmYoKQpwcmludChhMSkKCgoKfQpgYGAKCgoKCmBgYHtyfQoKZm9yZWNhc3RlZF9sb2NhdGlvbnMgPSB1bmlxdWUoZm9yZWNhc3RzX3dpdGhfdHJ1dGgkbG9jYXRpb24pCgp0YXJfdHlwZSA9ICJkZWF0aCIKCmZvciAobG9jIGluIGZvcmVjYXN0ZWRfbG9jYXRpb25zKXsKCnN0YXRlX251bSA9IGxvYyAjIjA0IgoKdGFyZ2V0X2ZvcmVjYXN0c193aXRoX3RydXRoIDwtCiAgZm9yZWNhc3RzX3dpdGhfdHJ1dGhbZ3JlcGwodGFyX3R5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9yZWNhc3RzX3dpdGhfdHJ1dGgkdGFyZ2V0KSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAoZm9yZWNhc3RzX3dpdGhfdHJ1dGgkbG9jYXRpb24gPT0gc3RhdGVfbnVtKSYKICAgICAgICAgICAgICAgICAgICAgICAgIGFzLkRhdGUoZm9yZWNhc3RzX3dpdGhfdHJ1dGgkZm9yZWNhc3RfZGF0ZSkKICAgICAgICAgICAgICAgICAgICAgICA+YXMuRGF0ZSgnMjAyMC0wNC0xMCcpLF0KCgpzdF9hYiA9IGZpcHMoc3RhdGVfbnVtLHRvPSJBYmJyZXZpYXRpb24iKQoKaWYgKGxvYyA9PSAiVVMiKXsKICBzdF9hYiA9ICJVUyIKfQpmY2FzdHNfd2lkZSA8LSBhc190aWJibGUodGFyZ2V0X2ZvcmVjYXN0c193aXRoX3RydXRoKSAlPiUKICAgIGZpbHRlcihxdWFudGlsZSAlaW4lIGMoMC4wMjUsIDAuMSwgMC4yNSwgMC41LCAwLjc1LCAwLjksIDAuOTc1KSkgJT4lCiAgICBtdXRhdGUod2Vla19haGVhZCA9IGFzLm51bWVyaWMoc3Vic3RyKHRhcmdldCwgMCwyKSkpICU+JQogICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHF1YW50aWxlLCBuYW1lc19wcmVmaXg9InEiKQoKCgpmaWdmaWxlbmFtZSA8LSBzcHJpbnRmKCJyZXN1bHRzX2ZpZ3VyZXMvJXNfJXNfcXVhbnRfZm9yZWNhc3RzLnBkZiIsc3RfYWIsdGFyX3R5cGUpCgpwZGYoZmlnZmlsZW5hbWUsIHdpZHRoID0gNSwgaGVpZ2h0ID0gMykKYTEgPC0gZ2dwbG90KGZjYXN0c193aWRlLGFlcyh4ID0gYXMuRGF0ZSh0YXJnZXRfZW5kX2RhdGUpKSkrCiAgICBnZW9tX2xpbmUoYWVzKHk9cTAuNSwgY29sb3I9Zm9yZWNhc3RfZGF0ZSwgZ3JvdXA9Zm9yZWNhc3RfZGF0ZSkpICsKICAgICMgZ2VvbV9yaWJib24oYWVzKHltaW49cTAuMSwgeW1heD1xMC45LCBmaWxsPWZvcmVjYXN0X2RhdGUsIGdyb3VwPWZvcmVjYXN0X2RhdGUpLCBhbHBoYT0uMykgKwogICAgZ2VvbV9yaWJib24oYWVzKHltaW49cTAuMDI1LCB5bWF4PXEwLjk3NSwgZmlsbD1mb3JlY2FzdF9kYXRlLCBncm91cD1mb3JlY2FzdF9kYXRlKSwgYWxwaGE9LjMpICsKICAgIGdlb21fcmliYm9uKGFlcyh5bWluPXEwLjI1LCB5bWF4PXEwLjc1LCBmaWxsPWZvcmVjYXN0X2RhdGUsIGdyb3VwPWZvcmVjYXN0X2RhdGUpLCBhbHBoYT0uMykgKwogICAgZ2VvbV9wb2ludChhZXMoeT10cnV0aCkpICsKICAgIGdlb21fbGluZShhZXMoeT10cnV0aCkpICsKICAgIHRoZW1lX2J3KCkgKyB4bGFiKCJEYXRlIikgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArIHlsYWIoc3ByaW50ZigiY3VtdWxhdGl2ZSAlcyIscGFzdGUwKHRhcl90eXBlLCJzIikpKSArCiAgICBnZ3RpdGxlKHNwcmludGYoIkN1bXVsYXRpdmUgJXMgaW4gJXMsIG9ic2VydmVkIGFuZCBmb3JlY2FzdGVkIixwYXN0ZTAodGFyX3R5cGUsInMiKSxzdF9hYikpCiMrCiAgICAjIGNvb3JkX2NhcnRlc2lhbih5bGltPWMoMjUwLCAyNTAwKSkKCnByaW50KGExKQpkZXYub2ZmKCkKcHJpbnQoYTEpCgoKCn0KYGBgCgoKCgoKCgpgYGB7cn0KCmdldF9jYWxpYnJhdGlvbl9wcm9wIDwtIGZ1bmN0aW9uKGZvcmVjYXN0c193aXRoX3RydXRoLGFscGhhX2xldmVsLGNvbmRpdGlvbnMsdGFyZ2V0X3R5cGU9ImRlYXRoIil7CiAgZm9yZWNhc3Rfc3ViMCA8LSBmb3JlY2FzdHNfd2l0aF90cnV0aFtncmVwbCh0YXJnZXRfdHlwZSxmb3JlY2FzdHNfd2l0aF90cnV0aCR0YXJnZXQpLF0KICBmb3JlY2FzdF9zdWJzZXQgPC0gc3Vic2V0KGZvcmVjYXN0X3N1YjAsZXZhbChjb25kaXRpb25zKSkKICBsYm91bmRzID0gc3Vic2V0KGZvcmVjYXN0X3N1YnNldCxxdWFudGlsZT09YWxwaGFfbGV2ZWwvMixzZWxlY3Q9InZhbHVlIikkdmFsdWUKICB1Ym91bmRzID0gIHN1YnNldChmb3JlY2FzdF9zdWJzZXQscXVhbnRpbGU9PTEgLSBhbHBoYV9sZXZlbC8yLHNlbGVjdD0idmFsdWUiKSR2YWx1ZQogIHRydXRoID0gIHN1YnNldChmb3JlY2FzdF9zdWJzZXQscXVhbnRpbGU9PTAuNSxzZWxlY3QgPSAidHJ1dGgiKSR0cnV0aAoKCiAgaW5fYm91bmRzIDwtICgodWJvdW5kcyAtIHRydXRoKT49MCkgJiAoKGxib3VuZHMtdHJ1dGgpPD0wKQoKICBwcm9wID0gc3VtKGluX2JvdW5kcykvbGVuZ3RoKHRydXRoKQoKICBjaGVja19kZiA8LSBjYmluZChsYm91bmRzLHVib3VuZHMsdHJ1dGgsaW5fYm91bmRzKQoKCiAgcmV0dXJuKHByb3ApCgp9CgoKCgpgYGAKCiMjIyBDYWxpYnJhdGlvbiBmaWd1cmVzCgpgYGB7ciBVUy1jYWxpYnJhdGlvbi1jYXNlc30KCmZvciAoaSBpbiAxOjQpewoKY3Vycl9sb2MgPSAiVVMiCgpjb25kX3RhcmdldCA9IHNwcmludGYoIiVzIHdrIGFoZWFkIGN1bSBjYXNlIixpKQpjb25kaXRpb24gPSBleHByZXNzaW9uKCh0YXJnZXQ9PWNvbmRfdGFyZ2V0KSYobG9jYXRpb249PSJVUyIpKQp0YXJfdHlwZSA9ICJjYXNlIgojIHRpdGxlX3RleHQgPC0gcGFzdGUoY3Vycl9sb2MsIi0iLHRhcl90eXBlKQp0aXRsZV90ZXh0IDwtIHNwcmludGYoIlVTIC0gJXMgd2VlayBhaGVhZCBjdW11bGF0aXZlIGNhc2VzIixpKQoKCgpjdXJyX2xvY19maXBzID0gZmlwcyhjdXJyX2xvYykKaWYgKGN1cnJfbG9jID09ICJVUyIpewogY3Vycl9sb2NfZmlwcyA9ICJVUyIKfQppZiAoY3Vycl9sb2NfZmlwcyA8IDEwKXsKICBjdXJyX2xvY19maXBzID0gcGFzdGUwKDAsY3Vycl9sb2NfZmlwcykKfWVsc2V7Y3Vycl9sb2NfZmlwcyA9IGFzLmNoYXJhY3RlcihjdXJyX2xvY19maXBzKX0KCiMgY29uZGl0aW9uID0gZXhwcmVzc2lvbigobG9jYXRpb249PWN1cnJfbG9jX2ZpcHMpKQoKYWxsX3Byb3BzIDwtIGMoKQphbGxfQ0lfd2lkdGhzIDwtIDEtYygwLjAyLDAuMDUsMC4xLDAuMiwwLjMsMC40LDAuNSwwLjYsMC43LDAuOCwwLjkpCgpmb3IgKGogaW4gMTpsZW5ndGgoYWxsX0NJX3dpZHRocykpewogIGN1cnJfYV9sZXZlbCA9IHJvdW5kKDEgLSBhbGxfQ0lfd2lkdGhzW2pdLDMpCiAgIyBjdXJyX3Byb3AgPC0gZ2V0X2NhbGlicmF0aW9uX3Byb3AoZm9yZWNhc3RzX3dpdGhfdHJ1dGgsY3Vycl9hX2xldmVsLAogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cHJlc3Npb24oKGxvY2F0aW9uID09IGN1cnJfbG9jX2ZpcHMpKSwiY2FzZSIpCiAgY3Vycl9wcm9wIDwtIGdldF9jYWxpYnJhdGlvbl9wcm9wKGZvcmVjYXN0c193aXRoX3RydXRoLGN1cnJfYV9sZXZlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZGl0aW9uLHRhcl90eXBlKQogIGFsbF9wcm9wcyA8LSBjKGFsbF9wcm9wcyxjdXJyX3Byb3ApCn0KCgpjYWxpYl9kZiA8LSBkYXRhLmZyYW1lKGV4cF9wcm9wID0gYWxsX0NJX3dpZHRocyxhY3RfcHJvcCA9IGFsbF9wcm9wcykKCgpmaWdmaWxlbmFtZV9jYWxpYnJhdGlvbiA8LSBzcHJpbnRmKCJyZXN1bHRzX2ZpZ3VyZXMvVVNfJXN3a2FoZWFkY2FzZXNfY2FsaWJyYXRpb24ucGRmIixpKQoKcGRmKGZpZ2ZpbGVuYW1lX2NhbGlicmF0aW9uLCB3aWR0aCA9IDUsIGhlaWdodCA9IDMpCmEzIDwtIGdncGxvdChkYXRhPWNhbGliX2RmKSsKICBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHg9ZXhwX3Byb3AseT1hY3RfcHJvcCkpKwogIGdlb21fYWJsaW5lKHNsb3BlPTEsaW50ZXJjZXB0PTApKwogIHhsaW0oYygwLDEpKSt5bGltKGMoMCwxKSkrCiAgeGxhYigiRXhwZWN0ZWQgUHJvcG9ydGlvbiIpKwogIHlsYWIoIkFjdHVhbCBQcm9wb3J0aW9uIikrCiAgZ2d0aXRsZSh0aXRsZV90ZXh0KQpwcmludChhMykKZGV2Lm9mZigpCn0KYGBgCgoKCmBgYHtyIFN0YXRlLWNhbGlicmF0aW9uLWNhc2VzfQoKZm9yIChpIGluIDE6NCl7CgoKCmNvbmRfdGFyZ2V0ID0gc3ByaW50ZigiJXMgd2sgYWhlYWQgY3VtIGNhc2UiLGkpCmNvbmRpdGlvbiA9IGV4cHJlc3Npb24oKHRhcmdldD09Y29uZF90YXJnZXQpJihsb2NhdGlvbiE9IlVTIikpCnRhcl90eXBlID0gImNhc2UiCiMgdGl0bGVfdGV4dCA8LSBwYXN0ZShjdXJyX2xvYywiLSIsdGFyX3R5cGUpCnRpdGxlX3RleHQgPC0gc3ByaW50ZigiU3RhdGUtbGV2ZWwgLSAlcyB3ZWVrIGFoZWFkIGN1bXVsYXRpdmUgY2FzZXMiLGkpCgoKCmFsbF9wcm9wcyA8LSBjKCkKYWxsX0NJX3dpZHRocyA8LSAxLWMoMC4wMiwwLjA1LDAuMSwwLjIsMC4zLDAuNCwwLjUsMC42LDAuNywwLjgsMC45KQoKZm9yIChqIGluIDE6bGVuZ3RoKGFsbF9DSV93aWR0aHMpKXsKICBjdXJyX2FfbGV2ZWwgPSByb3VuZCgxIC0gYWxsX0NJX3dpZHRoc1tqXSwzKQogICMgY3Vycl9wcm9wIDwtIGdldF9jYWxpYnJhdGlvbl9wcm9wKGZvcmVjYXN0c193aXRoX3RydXRoLGN1cnJfYV9sZXZlbCwKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHByZXNzaW9uKChsb2NhdGlvbiA9PSBjdXJyX2xvY19maXBzKSksImNhc2UiKQogIGN1cnJfcHJvcCA8LSBnZXRfY2FsaWJyYXRpb25fcHJvcChmb3JlY2FzdHNfd2l0aF90cnV0aCxjdXJyX2FfbGV2ZWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmRpdGlvbix0YXJfdHlwZSkKICBhbGxfcHJvcHMgPC0gYyhhbGxfcHJvcHMsY3Vycl9wcm9wKQp9CgoKY2FsaWJfZGYgPC0gZGF0YS5mcmFtZShleHBfcHJvcCA9IGFsbF9DSV93aWR0aHMsYWN0X3Byb3AgPSBhbGxfcHJvcHMpCgoKZmlnZmlsZW5hbWVfY2FsaWJyYXRpb24gPC0gc3ByaW50ZigicmVzdWx0c19maWd1cmVzL3N0YXRlLWxldmVsXyVzd2thaGVhZGNhc2VzX2NhbGlicmF0aW9uLnBkZiIsaSkKCnBkZihmaWdmaWxlbmFtZV9jYWxpYnJhdGlvbiwgd2lkdGggPSA1LCBoZWlnaHQgPSAzKQphMyA8LSBnZ3Bsb3QoZGF0YT1jYWxpYl9kZikrCiAgZ2VvbV9wb2ludChtYXBwaW5nPWFlcyh4PWV4cF9wcm9wLHk9YWN0X3Byb3ApKSsKICBnZW9tX2FibGluZShzbG9wZT0xLGludGVyY2VwdD0wKSsKICB4bGltKGMoMCwxKSkreWxpbShjKDAsMSkpKwogIHhsYWIoIkV4cGVjdGVkIFByb3BvcnRpb24iKSsKICB5bGFiKCJBY3R1YWwgUHJvcG9ydGlvbiIpKwogIGdndGl0bGUodGl0bGVfdGV4dCkKcHJpbnQoYTMpCmRldi5vZmYoKQp9CmBgYAoKCmBgYHtyIFVTLWNhbGlicmF0aW9uLWRlYXRoc30KCmZvciAoaSBpbiAxOjQpewoKY3Vycl9sb2MgPSAiVVMiCgpjb25kX3RhcmdldCA9IHNwcmludGYoIiVzIHdrIGFoZWFkIGN1bSBkZWF0aCIsaSkKY29uZGl0aW9uID0gZXhwcmVzc2lvbigodGFyZ2V0PT1jb25kX3RhcmdldCkmKGxvY2F0aW9uPT0iVVMiKSkKdGFyX3R5cGUgPSAiZGVhdGgiCiMgdGl0bGVfdGV4dCA8LSBwYXN0ZShjdXJyX2xvYywiLSIsdGFyX3R5cGUpCnRpdGxlX3RleHQgPC0gc3ByaW50ZigiVVMgLSAlcyB3ZWVrIGFoZWFkIGN1bXVsYXRpdmUgZGVhdGhzIixpKQoKCgpjdXJyX2xvY19maXBzID0gZmlwcyhjdXJyX2xvYykKaWYgKGN1cnJfbG9jID09ICJVUyIpewogY3Vycl9sb2NfZmlwcyA9ICJVUyIKfQppZiAoY3Vycl9sb2NfZmlwcyA8IDEwKXsKICBjdXJyX2xvY19maXBzID0gcGFzdGUwKDAsY3Vycl9sb2NfZmlwcykKfWVsc2V7Y3Vycl9sb2NfZmlwcyA9IGFzLmNoYXJhY3RlcihjdXJyX2xvY19maXBzKX0KCiMgY29uZGl0aW9uID0gZXhwcmVzc2lvbigobG9jYXRpb249PWN1cnJfbG9jX2ZpcHMpKQoKYWxsX3Byb3BzIDwtIGMoKQphbGxfQ0lfd2lkdGhzIDwtIDEtYygwLjAyLDAuMDUsMC4xLDAuMiwwLjMsMC40LDAuNSwwLjYsMC43LDAuOCwwLjkpCgpmb3IgKGogaW4gMTpsZW5ndGgoYWxsX0NJX3dpZHRocykpewogIGN1cnJfYV9sZXZlbCA9IHJvdW5kKDEgLSBhbGxfQ0lfd2lkdGhzW2pdLDMpCiAgIyBjdXJyX3Byb3AgPC0gZ2V0X2NhbGlicmF0aW9uX3Byb3AoZm9yZWNhc3RzX3dpdGhfdHJ1dGgsY3Vycl9hX2xldmVsLAogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cHJlc3Npb24oKGxvY2F0aW9uID09IGN1cnJfbG9jX2ZpcHMpKSwiY2FzZSIpCiAgY3Vycl9wcm9wIDwtIGdldF9jYWxpYnJhdGlvbl9wcm9wKGZvcmVjYXN0c193aXRoX3RydXRoLGN1cnJfYV9sZXZlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZGl0aW9uLHRhcl90eXBlKQogIGFsbF9wcm9wcyA8LSBjKGFsbF9wcm9wcyxjdXJyX3Byb3ApCn0KCgpjYWxpYl9kZiA8LSBkYXRhLmZyYW1lKGV4cF9wcm9wID0gYWxsX0NJX3dpZHRocyxhY3RfcHJvcCA9IGFsbF9wcm9wcykKCgpmaWdmaWxlbmFtZV9jYWxpYnJhdGlvbiA8LSBzcHJpbnRmKCJyZXN1bHRzX2ZpZ3VyZXMvVVNfJXN3a2FoZWFkZGVhdGhzX2NhbGlicmF0aW9uLnBkZiIsaSkKCnBkZihmaWdmaWxlbmFtZV9jYWxpYnJhdGlvbiwgd2lkdGggPSA1LCBoZWlnaHQgPSAzKQphMyA8LSBnZ3Bsb3QoZGF0YT1jYWxpYl9kZikrCiAgZ2VvbV9wb2ludChtYXBwaW5nPWFlcyh4PWV4cF9wcm9wLHk9YWN0X3Byb3ApKSsKICBnZW9tX2FibGluZShzbG9wZT0xLGludGVyY2VwdD0wKSsKICB4bGltKGMoMCwxKSkreWxpbShjKDAsMSkpKwogIHhsYWIoIkV4cGVjdGVkIFByb3BvcnRpb24iKSsKICB5bGFiKCJBY3R1YWwgUHJvcG9ydGlvbiIpKwogIGdndGl0bGUodGl0bGVfdGV4dCkKcHJpbnQoYTMpCmRldi5vZmYoKQp9CmBgYAoKCgpgYGB7ciBTdGF0ZS1jYWxpYnJhdGlvbi1kZWF0aHN9Cgpmb3IgKGkgaW4gMTo0KXsKCgoKY29uZF90YXJnZXQgPSBzcHJpbnRmKCIlcyB3ayBhaGVhZCBjdW0gZGVhdGgiLGkpCmNvbmRpdGlvbiA9IGV4cHJlc3Npb24oKHRhcmdldD09Y29uZF90YXJnZXQpJihsb2NhdGlvbiE9IlVTIikpCnRhcl90eXBlID0gImRlYXRoIgojIHRpdGxlX3RleHQgPC0gcGFzdGUoY3Vycl9sb2MsIi0iLHRhcl90eXBlKQp0aXRsZV90ZXh0IDwtIHNwcmludGYoIlN0YXRlLWxldmVsIC0gJXMgd2VlayBhaGVhZCBjdW11bGF0aXZlIGRlYXRocyIsaSkKCgoKYWxsX3Byb3BzIDwtIGMoKQphbGxfQ0lfd2lkdGhzIDwtIDEtYygwLjAyLDAuMDUsMC4xLDAuMiwwLjMsMC40LDAuNSwwLjYsMC43LDAuOCwwLjkpCgpmb3IgKGogaW4gMTpsZW5ndGgoYWxsX0NJX3dpZHRocykpewogIGN1cnJfYV9sZXZlbCA9IHJvdW5kKDEgLSBhbGxfQ0lfd2lkdGhzW2pdLDMpCiAgIyBjdXJyX3Byb3AgPC0gZ2V0X2NhbGlicmF0aW9uX3Byb3AoZm9yZWNhc3RzX3dpdGhfdHJ1dGgsY3Vycl9hX2xldmVsLAogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cHJlc3Npb24oKGxvY2F0aW9uID09IGN1cnJfbG9jX2ZpcHMpKSwiY2FzZSIpCiAgY3Vycl9wcm9wIDwtIGdldF9jYWxpYnJhdGlvbl9wcm9wKGZvcmVjYXN0c193aXRoX3RydXRoLGN1cnJfYV9sZXZlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZGl0aW9uLHRhcl90eXBlKQogIGFsbF9wcm9wcyA8LSBjKGFsbF9wcm9wcyxjdXJyX3Byb3ApCn0KCgpjYWxpYl9kZiA8LSBkYXRhLmZyYW1lKGV4cF9wcm9wID0gYWxsX0NJX3dpZHRocyxhY3RfcHJvcCA9IGFsbF9wcm9wcykKCgpmaWdmaWxlbmFtZV9jYWxpYnJhdGlvbiA8LSBzcHJpbnRmKCJyZXN1bHRzX2ZpZ3VyZXMvc3RhdGUtbGV2ZWxfJXN3a2FoZWFkZGVhdGhzX2NhbGlicmF0aW9uLnBkZiIsaSkKCnBkZihmaWdmaWxlbmFtZV9jYWxpYnJhdGlvbiwgd2lkdGggPSA1LCBoZWlnaHQgPSAzKQphMyA8LSBnZ3Bsb3QoZGF0YT1jYWxpYl9kZikrCiAgZ2VvbV9wb2ludChtYXBwaW5nPWFlcyh4PWV4cF9wcm9wLHk9YWN0X3Byb3ApKSsKICBnZW9tX2FibGluZShzbG9wZT0xLGludGVyY2VwdD0wKSsKICB4bGltKGMoMCwxKSkreWxpbShjKDAsMSkpKwogIHhsYWIoIkV4cGVjdGVkIFByb3BvcnRpb24iKSsKICB5bGFiKCJBY3R1YWwgUHJvcG9ydGlvbiIpKwogIGdndGl0bGUodGl0bGVfdGV4dCkKcHJpbnQoYTMpCmRldi5vZmYoKQp9CmBgYAoKCgoKCgpgYGB7ciBjb21iaW5lZC1VUy1TdGF0ZS1jYWxpYnJhdGlvbi1kZWF0aHN9CgoKY2FsaWJfZGYgPC0gZGF0YS5mcmFtZSgpCgpmb3IgKGkgaW4gMTo0KXsKCgoKY29uZF90YXJnZXQgPSBzcHJpbnRmKCIlcyB3ayBhaGVhZCBjdW0gZGVhdGgiLGkpCmNvbmRpdGlvbjEgPSBleHByZXNzaW9uKCh0YXJnZXQ9PWNvbmRfdGFyZ2V0KSYobG9jYXRpb24hPSJVUyIpKQpjb25kaXRpb24yID0gZXhwcmVzc2lvbigodGFyZ2V0PT1jb25kX3RhcmdldCkmKGxvY2F0aW9uPT0iVVMiKSkKdGFyX3R5cGUgPSAiZGVhdGgiCiMgdGl0bGVfdGV4dCA8LSBwYXN0ZShjdXJyX2xvYywiLSIsdGFyX3R5cGUpCnRpdGxlX3RleHQgPC0gc3ByaW50ZigiJXMgd2VlayBhaGVhZCIsaSkKCgoKYWxsX3Byb3BzMSA8LSBjKCkKYWxsX3Byb3BzMiA8LSBjKCkKYWxsX0NJX3dpZHRocyA8LSAxLWMoMC4wMiwwLjA1LDAuMSwwLjIsMC4zLDAuNCwwLjUsMC42LDAuNywwLjgsMC45KQoKZm9yIChqIGluIDE6bGVuZ3RoKGFsbF9DSV93aWR0aHMpKXsKICBjdXJyX2FfbGV2ZWwgPSByb3VuZCgxIC0gYWxsX0NJX3dpZHRoc1tqXSwzKQogICMgY3Vycl9wcm9wIDwtIGdldF9jYWxpYnJhdGlvbl9wcm9wKGZvcmVjYXN0c193aXRoX3RydXRoLGN1cnJfYV9sZXZlbCwKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHByZXNzaW9uKChsb2NhdGlvbiA9PSBjdXJyX2xvY19maXBzKSksImNhc2UiKQogIGN1cnJfcHJvcDEgPC0gZ2V0X2NhbGlicmF0aW9uX3Byb3AoZm9yZWNhc3RzX3dpdGhfdHJ1dGgsY3Vycl9hX2xldmVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25kaXRpb24xLHRhcl90eXBlKQogIGFsbF9wcm9wczEgPC0gYyhhbGxfcHJvcHMxLGN1cnJfcHJvcDEpCiAgCiAgY3Vycl9wcm9wMiA8LSBnZXRfY2FsaWJyYXRpb25fcHJvcChmb3JlY2FzdHNfd2l0aF90cnV0aCxjdXJyX2FfbGV2ZWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmRpdGlvbjIsdGFyX3R5cGUpCiAgYWxsX3Byb3BzMiA8LSBjKGFsbF9wcm9wczIsY3Vycl9wcm9wMikKfQoKCnRlbXAxIDwtIGRhdGEuZnJhbWUoZXhwX3Byb3AgPSBhbGxfQ0lfd2lkdGhzLGFjdF9wcm9wID0gYWxsX3Byb3BzMSxsZXZlbD0ic3RhdGUiLHRhcmdldD10aXRsZV90ZXh0KQp0ZW1wMiA8LSBkYXRhLmZyYW1lKGV4cF9wcm9wID0gYWxsX0NJX3dpZHRocyxhY3RfcHJvcCA9IGFsbF9wcm9wczIsbGV2ZWw9IlVTIix0YXJnZXQ9dGl0bGVfdGV4dCkKY2FsaWJfZGYgPC0gcmJpbmQoY2FsaWJfZGYsdGVtcDEsdGVtcDIpCgp9CgpmaWdmaWxlbmFtZV9jYWxpYnJhdGlvbiA8LSBzcHJpbnRmKCJyZXN1bHRzX2ZpZ3VyZXMvZGVhdGhzX2NhbGlicmF0aW9uLnBkZiIsaSkKCnBkZihmaWdmaWxlbmFtZV9jYWxpYnJhdGlvbiwgd2lkdGggPSA3LCBoZWlnaHQgPSA0KQphMyA8LSBnZ3Bsb3QoZGF0YT1jYWxpYl9kZikrCiAgZ2VvbV9wb2ludChtYXBwaW5nPWFlcyh4PWV4cF9wcm9wLHk9YWN0X3Byb3Asc2hhcGU9bGV2ZWwsY29sb3I9bGV2ZWwpLGNleD0xLjUpKwogICMgZ2VvbV9wb2ludChtYXBwaW5nPWFlcyh4PWV4cF9wcm9wLHk9dXNfcHJvcCxzaGFwZT0iYiIpLHNoYXBlPTE5KSsKICBnZW9tX2FibGluZShzbG9wZT0xLGludGVyY2VwdD0wKSsKICB4bGltKGMoMCwxKSkreWxpbShjKDAsMSkpKwogIHhsYWIoIkV4cGVjdGVkIFByb3BvcnRpb24iKSsKICB5bGFiKCJBY3R1YWwgUHJvcG9ydGlvbiIpKwogIGdndGl0bGUoIlBJIENhcHR1cmUgUmF0ZXMgZm9yIEN1bXVsYXRpdmUgRGVhdGggRm9yZWNhc3RzIikrCiAgZmFjZXRfd3JhcChmYWNldHM9dmFycyh0YXJnZXQpLG5yb3c9MikKCnByaW50KGEzKQpkZXYub2ZmKCkKcHJpbnQoYTMpCgpgYGAKCgoKCmBgYHtyIGNvbWJpbmVkLVVTLVN0YXRlLWNhbGlicmF0aW9uLWNhc2VzfQoKCgpjYWxpYl9kZiA8LSBkYXRhLmZyYW1lKCkKCmZvciAoaSBpbiAxOjQpewoKCgpjb25kX3RhcmdldCA9IHNwcmludGYoIiVzIHdrIGFoZWFkIGN1bSBjYXNlIixpKQpjb25kaXRpb24xID0gZXhwcmVzc2lvbigodGFyZ2V0PT1jb25kX3RhcmdldCkmKGxvY2F0aW9uIT0iVVMiKSkKY29uZGl0aW9uMiA9IGV4cHJlc3Npb24oKHRhcmdldD09Y29uZF90YXJnZXQpJihsb2NhdGlvbj09IlVTIikpCnRhcl90eXBlID0gImNhc2UiCiMgdGl0bGVfdGV4dCA8LSBwYXN0ZShjdXJyX2xvYywiLSIsdGFyX3R5cGUpCnRpdGxlX3RleHQgPC0gc3ByaW50ZigiJXMgd2VlayBhaGVhZCIsaSkKCgoKYWxsX3Byb3BzMSA8LSBjKCkKYWxsX3Byb3BzMiA8LSBjKCkKYWxsX0NJX3dpZHRocyA8LSAxLWMoMC4wMiwwLjA1LDAuMSwwLjIsMC4zLDAuNCwwLjUsMC42LDAuNywwLjgsMC45KQoKZm9yIChqIGluIDE6bGVuZ3RoKGFsbF9DSV93aWR0aHMpKXsKICBjdXJyX2FfbGV2ZWwgPSByb3VuZCgxIC0gYWxsX0NJX3dpZHRoc1tqXSwzKQogICMgY3Vycl9wcm9wIDwtIGdldF9jYWxpYnJhdGlvbl9wcm9wKGZvcmVjYXN0c193aXRoX3RydXRoLGN1cnJfYV9sZXZlbCwKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHByZXNzaW9uKChsb2NhdGlvbiA9PSBjdXJyX2xvY19maXBzKSksImNhc2UiKQogIGN1cnJfcHJvcDEgPC0gZ2V0X2NhbGlicmF0aW9uX3Byb3AoZm9yZWNhc3RzX3dpdGhfdHJ1dGgsY3Vycl9hX2xldmVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25kaXRpb24xLHRhcl90eXBlKQogIGFsbF9wcm9wczEgPC0gYyhhbGxfcHJvcHMxLGN1cnJfcHJvcDEpCiAgCiAgY3Vycl9wcm9wMiA8LSBnZXRfY2FsaWJyYXRpb25fcHJvcChmb3JlY2FzdHNfd2l0aF90cnV0aCxjdXJyX2FfbGV2ZWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmRpdGlvbjIsdGFyX3R5cGUpCiAgYWxsX3Byb3BzMiA8LSBjKGFsbF9wcm9wczIsY3Vycl9wcm9wMikKfQoKCnRlbXAxIDwtIGRhdGEuZnJhbWUoZXhwX3Byb3AgPSBhbGxfQ0lfd2lkdGhzLGFjdF9wcm9wID0gYWxsX3Byb3BzMSxsZXZlbD0ic3RhdGUiLHRhcmdldD10aXRsZV90ZXh0KQp0ZW1wMiA8LSBkYXRhLmZyYW1lKGV4cF9wcm9wID0gYWxsX0NJX3dpZHRocyxhY3RfcHJvcCA9IGFsbF9wcm9wczIsbGV2ZWw9IlVTIix0YXJnZXQ9dGl0bGVfdGV4dCkKY2FsaWJfZGYgPC0gcmJpbmQoY2FsaWJfZGYsdGVtcDEsdGVtcDIpCgp9CgpmaWdmaWxlbmFtZV9jYWxpYnJhdGlvbiA8LSBzcHJpbnRmKCJyZXN1bHRzX2ZpZ3VyZXMvY2FzZXNfY2FsaWJyYXRpb24ucGRmIixpKQoKcGRmKGZpZ2ZpbGVuYW1lX2NhbGlicmF0aW9uLCB3aWR0aCA9IDcsIGhlaWdodCA9IDQpCmEzIDwtIGdncGxvdChkYXRhPWNhbGliX2RmKSsKICBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHg9ZXhwX3Byb3AseT1hY3RfcHJvcCxzaGFwZT1sZXZlbCxjb2xvcj1sZXZlbCksY2V4PTEuNSkrCiAgIyBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHg9ZXhwX3Byb3AseT11c19wcm9wLHNoYXBlPSJiIiksc2hhcGU9MTkpKwogIGdlb21fYWJsaW5lKHNsb3BlPTEsaW50ZXJjZXB0PTApKwogIHhsaW0oYygwLDEpKSt5bGltKGMoMCwxKSkrCiAgeGxhYigiRXhwZWN0ZWQgUHJvcG9ydGlvbiIpKwogIHlsYWIoIkFjdHVhbCBQcm9wb3J0aW9uIikrCiAgZ2d0aXRsZSgiUEkgQ2FwdHVyZSBSYXRlcyBmb3IgQ3VtdWxhdGl2ZSBDYXNlIEZvcmVjYXN0cyIpKwogIGZhY2V0X3dyYXAoZmFjZXRzPXZhcnModGFyZ2V0KSxucm93PTIpCgpwcmludChhMykKZGV2Lm9mZigpCnByaW50KGEzKQoKYGBgCg==